mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-25 21:45:52 +00:00
Write improved forAllApiVersions used in NetworkOPs (#4833)
This commit is contained in:
@@ -218,7 +218,6 @@ install (
|
|||||||
install (
|
install (
|
||||||
FILES
|
FILES
|
||||||
src/ripple/json/JsonPropertyStream.h
|
src/ripple/json/JsonPropertyStream.h
|
||||||
src/ripple/json/MultivarJson.h
|
|
||||||
src/ripple/json/Object.h
|
src/ripple/json/Object.h
|
||||||
src/ripple/json/Output.h
|
src/ripple/json/Output.h
|
||||||
src/ripple/json/Writer.h
|
src/ripple/json/Writer.h
|
||||||
@@ -237,6 +236,7 @@ install (
|
|||||||
src/ripple/protocol/AccountID.h
|
src/ripple/protocol/AccountID.h
|
||||||
src/ripple/protocol/AMMCore.h
|
src/ripple/protocol/AMMCore.h
|
||||||
src/ripple/protocol/AmountConversions.h
|
src/ripple/protocol/AmountConversions.h
|
||||||
|
src/ripple/protocol/ApiVersion.h
|
||||||
src/ripple/protocol/Book.h
|
src/ripple/protocol/Book.h
|
||||||
src/ripple/protocol/BuildInfo.h
|
src/ripple/protocol/BuildInfo.h
|
||||||
src/ripple/protocol/ErrorCodes.h
|
src/ripple/protocol/ErrorCodes.h
|
||||||
@@ -252,6 +252,8 @@ install (
|
|||||||
src/ripple/protocol/KnownFormats.h
|
src/ripple/protocol/KnownFormats.h
|
||||||
src/ripple/protocol/LedgerFormats.h
|
src/ripple/protocol/LedgerFormats.h
|
||||||
src/ripple/protocol/LedgerHeader.h
|
src/ripple/protocol/LedgerHeader.h
|
||||||
|
src/ripple/protocol/MultiApiJson.h
|
||||||
|
src/ripple/protocol/NFTSyntheticSerializer.h
|
||||||
src/ripple/protocol/NFTokenID.h
|
src/ripple/protocol/NFTokenID.h
|
||||||
src/ripple/protocol/NFTokenOfferID.h
|
src/ripple/protocol/NFTokenOfferID.h
|
||||||
src/ripple/protocol/NFTSyntheticSerializer.h
|
src/ripple/protocol/NFTSyntheticSerializer.h
|
||||||
@@ -944,7 +946,6 @@ if (tests)
|
|||||||
src/test/json/Output_test.cpp
|
src/test/json/Output_test.cpp
|
||||||
src/test/json/Writer_test.cpp
|
src/test/json/Writer_test.cpp
|
||||||
src/test/json/json_value_test.cpp
|
src/test/json/json_value_test.cpp
|
||||||
src/test/json/MultivarJson_test.cpp
|
|
||||||
#[===============================[
|
#[===============================[
|
||||||
test sources:
|
test sources:
|
||||||
subdir: jtx
|
subdir: jtx
|
||||||
@@ -1042,11 +1043,13 @@ if (tests)
|
|||||||
test sources:
|
test sources:
|
||||||
subdir: protocol
|
subdir: protocol
|
||||||
#]===============================]
|
#]===============================]
|
||||||
|
src/test/protocol/ApiVersion_test.cpp
|
||||||
src/test/protocol/BuildInfo_test.cpp
|
src/test/protocol/BuildInfo_test.cpp
|
||||||
src/test/protocol/InnerObjectFormats_test.cpp
|
src/test/protocol/InnerObjectFormats_test.cpp
|
||||||
src/test/protocol/Issue_test.cpp
|
src/test/protocol/Issue_test.cpp
|
||||||
src/test/protocol/Hooks_test.cpp
|
src/test/protocol/Hooks_test.cpp
|
||||||
src/test/protocol/Memo_test.cpp
|
src/test/protocol/Memo_test.cpp
|
||||||
|
src/test/protocol/MultiApiJson_test.cpp
|
||||||
src/test/protocol/PublicKey_test.cpp
|
src/test/protocol/PublicKey_test.cpp
|
||||||
src/test/protocol/Quality_test.cpp
|
src/test/protocol/Quality_test.cpp
|
||||||
src/test/protocol/STAccount_test.cpp
|
src/test/protocol/STAccount_test.cpp
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ test.csf > ripple.json
|
|||||||
test.csf > ripple.protocol
|
test.csf > ripple.protocol
|
||||||
test.json > ripple.beast
|
test.json > ripple.beast
|
||||||
test.json > ripple.json
|
test.json > ripple.json
|
||||||
test.json > ripple.rpc
|
|
||||||
test.json > test.jtx
|
test.json > test.jtx
|
||||||
test.jtx > ripple.app
|
test.jtx > ripple.app
|
||||||
test.jtx > ripple.basics
|
test.jtx > ripple.basics
|
||||||
|
|||||||
@@ -54,8 +54,9 @@ BookListeners::publish(
|
|||||||
// Only publish jvObj if this is the first occurence
|
// Only publish jvObj if this is the first occurence
|
||||||
if (havePublished.emplace(p->getSeq()).second)
|
if (havePublished.emplace(p->getSeq()).second)
|
||||||
{
|
{
|
||||||
p->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
p->getApiVersion(), //
|
||||||
|
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
#ifndef RIPPLE_APP_LEDGER_BOOKLISTENERS_H_INCLUDED
|
#ifndef RIPPLE_APP_LEDGER_BOOKLISTENERS_H_INCLUDED
|
||||||
#define RIPPLE_APP_LEDGER_BOOKLISTENERS_H_INCLUDED
|
#define RIPPLE_APP_LEDGER_BOOKLISTENERS_H_INCLUDED
|
||||||
|
|
||||||
#include <ripple/json/MultivarJson.h>
|
|
||||||
#include <ripple/net/InfoSub.h>
|
#include <ripple/net/InfoSub.h>
|
||||||
|
#include <ripple/protocol/MultiApiJson.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include <ripple/app/ledger/AcceptedLedgerTx.h>
|
#include <ripple/app/ledger/AcceptedLedgerTx.h>
|
||||||
#include <ripple/app/ledger/BookListeners.h>
|
#include <ripple/app/ledger/BookListeners.h>
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/json/MultivarJson.h>
|
#include <ripple/protocol/MultiApiJson.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|||||||
@@ -168,9 +168,7 @@ fillJsonTx(
|
|||||||
if (validated)
|
if (validated)
|
||||||
{
|
{
|
||||||
auto const seq = fill.ledger.seq();
|
auto const seq = fill.ledger.seq();
|
||||||
txJson[jss::ledger_index] = (fill.context->apiVersion > 1)
|
txJson[jss::ledger_index] = seq;
|
||||||
? Json::Value(seq)
|
|
||||||
: Json::Value(std::to_string(seq));
|
|
||||||
if (fill.closeTime)
|
if (fill.closeTime)
|
||||||
txJson[jss::close_time_iso] = to_string_iso(*fill.closeTime);
|
txJson[jss::close_time_iso] = to_string_iso(*fill.closeTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,6 @@
|
|||||||
#include <ripple/consensus/ConsensusParms.h>
|
#include <ripple/consensus/ConsensusParms.h>
|
||||||
#include <ripple/crypto/RFC1751.h>
|
#include <ripple/crypto/RFC1751.h>
|
||||||
#include <ripple/crypto/csprng.h>
|
#include <ripple/crypto/csprng.h>
|
||||||
#include <ripple/json/MultivarJson.h>
|
|
||||||
#include <ripple/json/to_string.h>
|
#include <ripple/json/to_string.h>
|
||||||
#include <ripple/nodestore/DatabaseShard.h>
|
#include <ripple/nodestore/DatabaseShard.h>
|
||||||
#include <ripple/overlay/Cluster.h>
|
#include <ripple/overlay/Cluster.h>
|
||||||
@@ -60,6 +59,7 @@
|
|||||||
#include <ripple/overlay/predicates.h>
|
#include <ripple/overlay/predicates.h>
|
||||||
#include <ripple/protocol/BuildInfo.h>
|
#include <ripple/protocol/BuildInfo.h>
|
||||||
#include <ripple/protocol/Feature.h>
|
#include <ripple/protocol/Feature.h>
|
||||||
|
#include <ripple/protocol/MultiApiJson.h>
|
||||||
#include <ripple/protocol/RPCErr.h>
|
#include <ripple/protocol/RPCErr.h>
|
||||||
#include <ripple/protocol/STParsedJSON.h>
|
#include <ripple/protocol/STParsedJSON.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
@@ -68,7 +68,6 @@
|
|||||||
#include <ripple/rpc/BookChanges.h>
|
#include <ripple/rpc/BookChanges.h>
|
||||||
#include <ripple/rpc/DeliveredAmount.h>
|
#include <ripple/rpc/DeliveredAmount.h>
|
||||||
#include <ripple/rpc/ServerHandler.h>
|
#include <ripple/rpc/ServerHandler.h>
|
||||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
|
||||||
#include <boost/asio/ip/host_name.hpp>
|
#include <boost/asio/ip/host_name.hpp>
|
||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
|
|
||||||
@@ -2213,11 +2212,11 @@ NetworkOPsImp::pubValidation(std::shared_ptr<STValidation> const& val)
|
|||||||
// NOTE Use MultiApiJson to publish two slightly different JSON objects
|
// NOTE Use MultiApiJson to publish two slightly different JSON objects
|
||||||
// for consumers supporting different API versions
|
// for consumers supporting different API versions
|
||||||
MultiApiJson multiObj{jvObj};
|
MultiApiJson multiObj{jvObj};
|
||||||
visit<RPC::apiMinimumSupportedVersion, RPC::apiMaximumValidVersion>(
|
multiObj.visit(
|
||||||
multiObj, //
|
RPC::apiVersion<1>, //
|
||||||
[](Json::Value& jvTx, unsigned int apiVersion) {
|
[](Json::Value& jvTx) {
|
||||||
// Type conversion for older API versions to string
|
// Type conversion for older API versions to string
|
||||||
if (jvTx.isMember(jss::ledger_index) && apiVersion < 2)
|
if (jvTx.isMember(jss::ledger_index))
|
||||||
{
|
{
|
||||||
jvTx[jss::ledger_index] =
|
jvTx[jss::ledger_index] =
|
||||||
std::to_string(jvTx[jss::ledger_index].asUInt());
|
std::to_string(jvTx[jss::ledger_index].asUInt());
|
||||||
@@ -2229,9 +2228,9 @@ NetworkOPsImp::pubValidation(std::shared_ptr<STValidation> const& val)
|
|||||||
{
|
{
|
||||||
if (auto p = i->second.lock())
|
if (auto p = i->second.lock())
|
||||||
{
|
{
|
||||||
p->send(
|
multiObj.visit(
|
||||||
multiObj.select(apiVersionSelector(p->getApiVersion())),
|
p->getApiVersion(), //
|
||||||
true);
|
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2769,8 +2768,9 @@ NetworkOPsImp::pubProposedTransaction(
|
|||||||
|
|
||||||
if (p)
|
if (p)
|
||||||
{
|
{
|
||||||
p->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
p->getApiVersion(), //
|
||||||
|
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -3167,13 +3167,14 @@ NetworkOPsImp::transJson(
|
|||||||
|
|
||||||
std::string const hash = to_string(transaction->getTransactionID());
|
std::string const hash = to_string(transaction->getTransactionID());
|
||||||
MultiApiJson multiObj{jvObj};
|
MultiApiJson multiObj{jvObj};
|
||||||
visit<RPC::apiMinimumSupportedVersion, RPC::apiMaximumValidVersion>(
|
forAllApiVersions(
|
||||||
multiObj, //
|
multiObj.visit(), //
|
||||||
[&](Json::Value& jvTx, unsigned int apiVersion) {
|
[&]<unsigned Version>(
|
||||||
|
Json::Value& jvTx, std::integral_constant<unsigned, Version>) {
|
||||||
RPC::insertDeliverMax(
|
RPC::insertDeliverMax(
|
||||||
jvTx[jss::transaction], transaction->getTxnType(), apiVersion);
|
jvTx[jss::transaction], transaction->getTxnType(), Version);
|
||||||
|
|
||||||
if (apiVersion > 1)
|
if constexpr (Version > 1)
|
||||||
{
|
{
|
||||||
jvTx[jss::tx_json] = jvTx.removeMember(jss::transaction);
|
jvTx[jss::tx_json] = jvTx.removeMember(jss::transaction);
|
||||||
jvTx[jss::hash] = hash;
|
jvTx[jss::hash] = hash;
|
||||||
@@ -3210,8 +3211,9 @@ NetworkOPsImp::pubValidatedTransaction(
|
|||||||
|
|
||||||
if (p)
|
if (p)
|
||||||
{
|
{
|
||||||
p->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
p->getApiVersion(), //
|
||||||
|
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -3226,8 +3228,9 @@ NetworkOPsImp::pubValidatedTransaction(
|
|||||||
|
|
||||||
if (p)
|
if (p)
|
||||||
{
|
{
|
||||||
p->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
p->getApiVersion(), //
|
||||||
|
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -3347,9 +3350,9 @@ NetworkOPsImp::pubAccountTransaction(
|
|||||||
|
|
||||||
for (InfoSub::ref isrListener : notify)
|
for (InfoSub::ref isrListener : notify)
|
||||||
{
|
{
|
||||||
isrListener->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(isrListener->getApiVersion())),
|
isrListener->getApiVersion(), //
|
||||||
true);
|
[&](Json::Value const& jv) { isrListener->send(jv, true); });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last)
|
if (last)
|
||||||
@@ -3366,9 +3369,9 @@ NetworkOPsImp::pubAccountTransaction(
|
|||||||
|
|
||||||
jvObj.set(jss::account_history_tx_index, index->forwardTxIndex_++);
|
jvObj.set(jss::account_history_tx_index, index->forwardTxIndex_++);
|
||||||
|
|
||||||
info.sink_->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(info.sink_->getApiVersion())),
|
info.sink_->getApiVersion(), //
|
||||||
true);
|
[&](Json::Value const& jv) { info.sink_->send(jv, true); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3426,9 +3429,9 @@ NetworkOPsImp::pubProposedAccountTransaction(
|
|||||||
MultiApiJson jvObj = transJson(tx, result, false, ledger, std::nullopt);
|
MultiApiJson jvObj = transJson(tx, result, false, ledger, std::nullopt);
|
||||||
|
|
||||||
for (InfoSub::ref isrListener : notify)
|
for (InfoSub::ref isrListener : notify)
|
||||||
isrListener->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(isrListener->getApiVersion())),
|
isrListener->getApiVersion(), //
|
||||||
true);
|
[&](Json::Value const& jv) { isrListener->send(jv, true); });
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
jvObj.isMember(jss::account_history_tx_stream) ==
|
jvObj.isMember(jss::account_history_tx_stream) ==
|
||||||
@@ -3439,9 +3442,9 @@ NetworkOPsImp::pubProposedAccountTransaction(
|
|||||||
if (index->forwardTxIndex_ == 0 && !index->haveHistorical_)
|
if (index->forwardTxIndex_ == 0 && !index->haveHistorical_)
|
||||||
jvObj.set(jss::account_history_tx_first, true);
|
jvObj.set(jss::account_history_tx_first, true);
|
||||||
jvObj.set(jss::account_history_tx_index, index->forwardTxIndex_++);
|
jvObj.set(jss::account_history_tx_index, index->forwardTxIndex_++);
|
||||||
info.sink_->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(info.sink_->getApiVersion())),
|
info.sink_->getApiVersion(), //
|
||||||
true);
|
[&](Json::Value const& jv) { info.sink_->send(jv, true); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3647,9 +3650,9 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo)
|
|||||||
bool unsubscribe) -> bool {
|
bool unsubscribe) -> bool {
|
||||||
if (auto sptr = subInfo.sinkWptr_.lock())
|
if (auto sptr = subInfo.sinkWptr_.lock())
|
||||||
{
|
{
|
||||||
sptr->send(
|
jvObj.visit(
|
||||||
jvObj.select(apiVersionSelector(sptr->getApiVersion())),
|
sptr->getApiVersion(), //
|
||||||
true);
|
[&](Json::Value const& jv) { sptr->send(jv, true); });
|
||||||
|
|
||||||
if (unsubscribe)
|
if (unsubscribe)
|
||||||
unsubAccountHistory(sptr, accountId, false);
|
unsubAccountHistory(sptr, accountId, false);
|
||||||
|
|||||||
@@ -1,150 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of rippled: https://github.com/ripple/rippled
|
|
||||||
Copyright (c) 2023 Ripple Labs Inc.
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or 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.
|
|
||||||
*/
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
#ifndef RIPPLE_JSON_MULTIVARJSON_H_INCLUDED
|
|
||||||
#define RIPPLE_JSON_MULTIVARJSON_H_INCLUDED
|
|
||||||
|
|
||||||
#include <ripple/json/json_value.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cassert>
|
|
||||||
#include <concepts>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace ripple {
|
|
||||||
template <std::size_t Size>
|
|
||||||
struct MultivarJson
|
|
||||||
{
|
|
||||||
std::array<Json::Value, Size> val = {};
|
|
||||||
constexpr static std::size_t size = Size;
|
|
||||||
|
|
||||||
explicit MultivarJson(Json::Value const& init = {})
|
|
||||||
{
|
|
||||||
if (init == Json::Value{})
|
|
||||||
return; // All elements are already default-initialized
|
|
||||||
for (auto& v : val)
|
|
||||||
v = init;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value const&
|
|
||||||
select(auto&& selector) const
|
|
||||||
requires std::same_as<std::size_t, decltype(selector())>
|
|
||||||
{
|
|
||||||
auto const index = selector();
|
|
||||||
assert(index < size);
|
|
||||||
return val[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set(const char* key,
|
|
||||||
auto const&
|
|
||||||
v) requires std::constructible_from<Json::Value, decltype(v)>
|
|
||||||
{
|
|
||||||
for (auto& a : this->val)
|
|
||||||
a[key] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally not using class enum here, MultivarJson is scope enough
|
|
||||||
enum IsMemberResult : int { none = 0, some, all };
|
|
||||||
|
|
||||||
[[nodiscard]] IsMemberResult
|
|
||||||
isMember(const char* key) const
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
for (auto& a : this->val)
|
|
||||||
if (a.isMember(key))
|
|
||||||
count += 1;
|
|
||||||
|
|
||||||
return (count == 0 ? none : (count < size ? some : all));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wrapper for Json for all supported API versions.
|
|
||||||
using MultiApiJson = MultivarJson<3>;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
NOTE:
|
|
||||||
|
|
||||||
If a future API version change adds another possible format, change the size of
|
|
||||||
`MultiApiJson`, and update `apiVersionSelector()` to return the appropriate
|
|
||||||
selection value for the new `apiVersion` and higher.
|
|
||||||
|
|
||||||
The more different JSON formats we support, the more CPU cycles we need to
|
|
||||||
prepare JSON for different API versions e.g. when publishing streams to
|
|
||||||
`subscribe` clients. Hence it is desirable to keep MultiApiJson small and
|
|
||||||
instead fully deprecate and remove support for old API versions. For example, if
|
|
||||||
we removed support for API version 1 and added a different format for API
|
|
||||||
version 3, the `apiVersionSelector` would change to
|
|
||||||
`static_cast<std::size_t>(apiVersion > 2)`
|
|
||||||
|
|
||||||
Such hypothetical change should correspond with change in RPCHelpers.h
|
|
||||||
`apiMinimumSupportedVersion = 2;`
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Helper to create appropriate selector for indexing MultiApiJson by apiVersion
|
|
||||||
constexpr auto
|
|
||||||
apiVersionSelector(unsigned int apiVersion) noexcept
|
|
||||||
{
|
|
||||||
return [apiVersion]() constexpr
|
|
||||||
{
|
|
||||||
return static_cast<std::size_t>(
|
|
||||||
apiVersion <= 1 //
|
|
||||||
? 0 //
|
|
||||||
: (apiVersion <= 2 //
|
|
||||||
? 1 //
|
|
||||||
: 2));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to execute a callback for every version. Want both min and max version
|
|
||||||
// provided explicitly, so user will know to do update `size` when they change
|
|
||||||
template <
|
|
||||||
unsigned int minVer,
|
|
||||||
unsigned int maxVer,
|
|
||||||
std::size_t size,
|
|
||||||
typename Fn>
|
|
||||||
requires //
|
|
||||||
(maxVer >= minVer) && //
|
|
||||||
(size == maxVer + 1 - minVer) && //
|
|
||||||
(apiVersionSelector(minVer)() == 0) && //
|
|
||||||
(apiVersionSelector(maxVer)() + 1 == size) && //
|
|
||||||
requires(Json::Value& json, Fn fn)
|
|
||||||
{
|
|
||||||
fn(json, static_cast<unsigned int>(1));
|
|
||||||
}
|
|
||||||
void
|
|
||||||
visit(MultivarJson<size>& json, Fn fn)
|
|
||||||
{
|
|
||||||
[&]<std::size_t... offset>(std::index_sequence<offset...>)
|
|
||||||
{
|
|
||||||
static_assert(((apiVersionSelector(minVer + offset)() >= 0) && ...));
|
|
||||||
static_assert(((apiVersionSelector(minVer + offset)() < size) && ...));
|
|
||||||
(fn(json.val[apiVersionSelector(minVer + offset)()], minVer + offset),
|
|
||||||
...);
|
|
||||||
}
|
|
||||||
(std::make_index_sequence<size>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ripple
|
|
||||||
|
|
||||||
#endif
|
|
||||||
119
src/ripple/protocol/ApiVersion.h
Normal file
119
src/ripple/protocol/ApiVersion.h
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2023 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or 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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef RIPPLE_PROTOCOL_APIVERSION_H_INCLUDED
|
||||||
|
#define RIPPLE_PROTOCOL_APIVERSION_H_INCLUDED
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API version numbers used in later API versions
|
||||||
|
*
|
||||||
|
* Requests with a version number in the range
|
||||||
|
* [apiMinimumSupportedVersion, apiMaximumSupportedVersion]
|
||||||
|
* are supported.
|
||||||
|
*
|
||||||
|
* If [beta_rpc_api] is enabled in config, the version numbers
|
||||||
|
* in the range [apiMinimumSupportedVersion, apiBetaVersion]
|
||||||
|
* are supported.
|
||||||
|
*
|
||||||
|
* Network Requests without explicit version numbers use
|
||||||
|
* apiVersionIfUnspecified. apiVersionIfUnspecified is 1,
|
||||||
|
* because all the RPC requests with a version >= 2 must
|
||||||
|
* explicitly specify the version in the requests.
|
||||||
|
* Note that apiVersionIfUnspecified will be lower than
|
||||||
|
* apiMinimumSupportedVersion when we stop supporting API
|
||||||
|
* version 1.
|
||||||
|
*
|
||||||
|
* Command line Requests use apiCommandLineVersion.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace RPC {
|
||||||
|
|
||||||
|
template <unsigned int Version>
|
||||||
|
constexpr static std::integral_constant<unsigned, Version> apiVersion = {};
|
||||||
|
|
||||||
|
constexpr static auto apiInvalidVersion = apiVersion<0>;
|
||||||
|
constexpr static auto apiMinimumSupportedVersion = apiVersion<1>;
|
||||||
|
constexpr static auto apiMaximumSupportedVersion = apiVersion<2>;
|
||||||
|
constexpr static auto apiVersionIfUnspecified = apiVersion<1>;
|
||||||
|
constexpr static auto apiCommandLineVersion =
|
||||||
|
apiVersion<1>; // TODO Bump to 2 later
|
||||||
|
constexpr static auto apiBetaVersion = apiVersion<3>;
|
||||||
|
constexpr static auto apiMaximumValidVersion = apiBetaVersion;
|
||||||
|
|
||||||
|
static_assert(apiInvalidVersion < apiMinimumSupportedVersion);
|
||||||
|
static_assert(
|
||||||
|
apiVersionIfUnspecified >= apiMinimumSupportedVersion &&
|
||||||
|
apiVersionIfUnspecified <= apiMaximumSupportedVersion);
|
||||||
|
static_assert(
|
||||||
|
apiCommandLineVersion >= apiMinimumSupportedVersion &&
|
||||||
|
apiCommandLineVersion <= apiMaximumSupportedVersion);
|
||||||
|
static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion);
|
||||||
|
static_assert(apiBetaVersion >= apiMaximumSupportedVersion);
|
||||||
|
static_assert(apiMaximumValidVersion >= apiMaximumSupportedVersion);
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
|
|
||||||
|
template <unsigned minVer, unsigned maxVer, typename Fn, typename... Args>
|
||||||
|
void
|
||||||
|
forApiVersions(Fn const& fn, Args&&... args) requires //
|
||||||
|
(maxVer >= minVer) && //
|
||||||
|
(minVer >= RPC::apiMinimumSupportedVersion) && //
|
||||||
|
(RPC::apiMaximumValidVersion >= maxVer) &&
|
||||||
|
requires
|
||||||
|
{
|
||||||
|
fn(std::integral_constant<unsigned int, minVer>{},
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
fn(std::integral_constant<unsigned int, maxVer>{},
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
constexpr auto size = maxVer + 1 - minVer;
|
||||||
|
[&]<std::size_t... offset>(std::index_sequence<offset...>)
|
||||||
|
{
|
||||||
|
(((void)fn(
|
||||||
|
std::integral_constant<unsigned int, minVer + offset>{},
|
||||||
|
std::forward<Args>(args)...)),
|
||||||
|
...);
|
||||||
|
}
|
||||||
|
(std::make_index_sequence<size>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn, typename... Args>
|
||||||
|
void
|
||||||
|
forAllApiVersions(Fn const& fn, Args&&... args) requires requires
|
||||||
|
{
|
||||||
|
forApiVersions<
|
||||||
|
RPC::apiMinimumSupportedVersion,
|
||||||
|
RPC::apiMaximumValidVersion>(fn, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
forApiVersions<
|
||||||
|
RPC::apiMinimumSupportedVersion,
|
||||||
|
RPC::apiMaximumValidVersion>(fn, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
247
src/ripple/protocol/MultiApiJson.h
Normal file
247
src/ripple/protocol/MultiApiJson.h
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2023 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or 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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef RIPPLE_JSON_MULTIAPIJSON_H_INCLUDED
|
||||||
|
#define RIPPLE_JSON_MULTIAPIJSON_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/json/json_value.h>
|
||||||
|
#include <ripple/protocol/ApiVersion.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_integral_constant = false;
|
||||||
|
template <typename I, auto A>
|
||||||
|
constexpr bool is_integral_constant<std::integral_constant<I, A>&> = true;
|
||||||
|
template <typename I, auto A>
|
||||||
|
constexpr bool is_integral_constant<std::integral_constant<I, A> const&> = true;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept some_integral_constant = detail::is_integral_constant<T&>;
|
||||||
|
|
||||||
|
// This class is designed to wrap a collection of _almost_ identical Json::Value
|
||||||
|
// objects, indexed by version (i.e. there is some mapping of version to object
|
||||||
|
// index). It is used e.g. when we need to publish JSON data to users supporting
|
||||||
|
// different API versions. We allow manipulation and inspection of all objects
|
||||||
|
// at once with `isMember` and `set`, and also individual inspection and updates
|
||||||
|
// of an object selected by the user by version, using `visitor_t` nested type.
|
||||||
|
template <unsigned MinVer, unsigned MaxVer>
|
||||||
|
struct MultiApiJson
|
||||||
|
{
|
||||||
|
static_assert(MinVer <= MaxVer);
|
||||||
|
|
||||||
|
static constexpr auto
|
||||||
|
valid(unsigned int v) noexcept -> bool
|
||||||
|
{
|
||||||
|
return v >= MinVer && v <= MaxVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto
|
||||||
|
index(unsigned int v) noexcept -> std::size_t
|
||||||
|
{
|
||||||
|
return (v < MinVer) ? 0 : static_cast<std::size_t>(v - MinVer);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr static std::size_t size = MaxVer + 1 - MinVer;
|
||||||
|
std::array<Json::Value, size> val = {};
|
||||||
|
|
||||||
|
explicit MultiApiJson(Json::Value const& init = {})
|
||||||
|
{
|
||||||
|
if (init == Json::Value{})
|
||||||
|
return; // All elements are already default-initialized
|
||||||
|
for (auto& v : val)
|
||||||
|
v = init;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set(const char* key,
|
||||||
|
auto const&
|
||||||
|
v) requires std::constructible_from<Json::Value, decltype(v)>
|
||||||
|
{
|
||||||
|
for (auto& a : this->val)
|
||||||
|
a[key] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intentionally not using class enum here, MultivarJson is scope enough
|
||||||
|
enum IsMemberResult : int { none = 0, some, all };
|
||||||
|
|
||||||
|
[[nodiscard]] IsMemberResult
|
||||||
|
isMember(const char* key) const
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (auto& a : this->val)
|
||||||
|
if (a.isMember(key))
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
return (count == 0 ? none : (count < size ? some : all));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr struct visitor_t final
|
||||||
|
{
|
||||||
|
// integral_constant version, extra arguments
|
||||||
|
template <
|
||||||
|
typename Json,
|
||||||
|
unsigned int Version,
|
||||||
|
typename... Args,
|
||||||
|
typename Fn>
|
||||||
|
requires std::same_as<std::remove_cvref_t<Json>, MultiApiJson> auto
|
||||||
|
operator()(
|
||||||
|
Json& json,
|
||||||
|
std::integral_constant<unsigned int, Version> const version,
|
||||||
|
Fn fn,
|
||||||
|
Args&&... args) const
|
||||||
|
-> std::invoke_result_t<
|
||||||
|
Fn,
|
||||||
|
decltype(json.val[0]),
|
||||||
|
std::integral_constant<unsigned int, Version>,
|
||||||
|
Args&&...>
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
valid(Version) && index(Version) >= 0 && index(Version) < size);
|
||||||
|
return std::invoke(
|
||||||
|
fn,
|
||||||
|
json.val[index(Version)],
|
||||||
|
version,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// integral_constant version, Json only
|
||||||
|
template <typename Json, unsigned int Version, typename Fn>
|
||||||
|
requires std::same_as<std::remove_cvref_t<Json>, MultiApiJson> auto
|
||||||
|
operator()(
|
||||||
|
Json& json,
|
||||||
|
std::integral_constant<unsigned int, Version> const,
|
||||||
|
Fn fn) const -> std::invoke_result_t<Fn, decltype(json.val[0])>
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
valid(Version) && index(Version) >= 0 && index(Version) < size);
|
||||||
|
return std::invoke(fn, json.val[index(Version)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsigned int version, extra arguments
|
||||||
|
template <
|
||||||
|
typename Json,
|
||||||
|
typename Version,
|
||||||
|
typename... Args,
|
||||||
|
typename Fn>
|
||||||
|
requires(!some_integral_constant<Version>) &&
|
||||||
|
std::convertible_to<Version, unsigned>&& std::same_as<
|
||||||
|
std::remove_cvref_t<Json>,
|
||||||
|
MultiApiJson> auto
|
||||||
|
operator()(Json& json, Version version, Fn fn, Args&&... args) const
|
||||||
|
-> std::
|
||||||
|
invoke_result_t<Fn, decltype(json.val[0]), Version, Args&&...>
|
||||||
|
{
|
||||||
|
assert(
|
||||||
|
valid(version) && index(version) >= 0 && index(version) < size);
|
||||||
|
return std::invoke(
|
||||||
|
fn,
|
||||||
|
json.val[index(version)],
|
||||||
|
version,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsigned int version, Json only
|
||||||
|
template <typename Json, typename Version, typename Fn>
|
||||||
|
requires(!some_integral_constant<Version>) &&
|
||||||
|
std::convertible_to<Version, unsigned>&& std::
|
||||||
|
same_as<std::remove_cvref_t<Json>, MultiApiJson> auto
|
||||||
|
operator()(Json& json, Version version, Fn fn) const
|
||||||
|
-> std::invoke_result_t<Fn, decltype(json.val[0])>
|
||||||
|
{
|
||||||
|
assert(
|
||||||
|
valid(version) && index(version) >= 0 && index(version) < size);
|
||||||
|
return std::invoke(fn, json.val[index(version)]);
|
||||||
|
}
|
||||||
|
} visitor = {};
|
||||||
|
|
||||||
|
auto
|
||||||
|
visit()
|
||||||
|
{
|
||||||
|
return [self = this](auto... args) requires requires
|
||||||
|
{
|
||||||
|
visitor(
|
||||||
|
std::declval<MultiApiJson&>(),
|
||||||
|
std::declval<decltype(args)>()...);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
return visitor(*self, std::forward<decltype(args)>(args)...);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto
|
||||||
|
visit() const
|
||||||
|
{
|
||||||
|
return [self = this](auto... args) requires requires
|
||||||
|
{
|
||||||
|
visitor(
|
||||||
|
std::declval<MultiApiJson const&>(),
|
||||||
|
std::declval<decltype(args)>()...);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
return visitor(*self, std::forward<decltype(args)>(args)...);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
auto
|
||||||
|
visit(Args... args)
|
||||||
|
-> std::invoke_result_t<visitor_t, MultiApiJson&, Args...> requires(
|
||||||
|
sizeof...(args) > 0) &&
|
||||||
|
requires
|
||||||
|
{
|
||||||
|
visitor(*this, std::forward<decltype(args)>(args)...);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
return visitor(*this, std::forward<decltype(args)>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
auto
|
||||||
|
visit(Args... args) const -> std::
|
||||||
|
invoke_result_t<visitor_t, MultiApiJson const&, Args...> requires(
|
||||||
|
sizeof...(args) > 0) &&
|
||||||
|
requires
|
||||||
|
{
|
||||||
|
visitor(*this, std::forward<decltype(args)>(args)...);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
return visitor(*this, std::forward<decltype(args)>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
// Wrapper for Json for all supported API versions.
|
||||||
|
using MultiApiJson = detail::
|
||||||
|
MultiApiJson<RPC::apiMinimumSupportedVersion, RPC::apiMaximumValidVersion>;
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <ripple/beast/core/SemanticVersion.h>
|
#include <ripple/beast/core/SemanticVersion.h>
|
||||||
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.pb.h>
|
#include <ripple/proto/org/xrpl/rpc/v1/xrp_ledger.pb.h>
|
||||||
|
#include <ripple/protocol/ApiVersion.h>
|
||||||
#include <ripple/protocol/TxMeta.h>
|
#include <ripple/protocol/TxMeta.h>
|
||||||
|
|
||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
#include <ripple/rpc/Context.h>
|
#include <ripple/rpc/Context.h>
|
||||||
#include <ripple/rpc/Status.h>
|
#include <ripple/rpc/Status.h>
|
||||||
#include <ripple/rpc/impl/Tuning.h>
|
#include <ripple/rpc/impl/Tuning.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
@@ -207,41 +209,6 @@ extern beast::SemanticVersion const firstVersion;
|
|||||||
extern beast::SemanticVersion const goodVersion;
|
extern beast::SemanticVersion const goodVersion;
|
||||||
extern beast::SemanticVersion const lastVersion;
|
extern beast::SemanticVersion const lastVersion;
|
||||||
|
|
||||||
/**
|
|
||||||
* API version numbers used in later API versions
|
|
||||||
*
|
|
||||||
* Requests with a version number in the range
|
|
||||||
* [apiMinimumSupportedVersion, apiMaximumSupportedVersion]
|
|
||||||
* are supported.
|
|
||||||
*
|
|
||||||
* If [beta_rpc_api] is enabled in config, the version numbers
|
|
||||||
* in the range [apiMinimumSupportedVersion, apiBetaVersion]
|
|
||||||
* are supported.
|
|
||||||
*
|
|
||||||
* Network Requests without explicit version numbers use
|
|
||||||
* apiVersionIfUnspecified. apiVersionIfUnspecified is 1,
|
|
||||||
* because all the RPC requests with a version >= 2 must
|
|
||||||
* explicitly specify the version in the requests.
|
|
||||||
* Note that apiVersionIfUnspecified will be lower than
|
|
||||||
* apiMinimumSupportedVersion when we stop supporting API
|
|
||||||
* version 1.
|
|
||||||
*
|
|
||||||
* Command line Requests use apiMaximumSupportedVersion.
|
|
||||||
*/
|
|
||||||
|
|
||||||
constexpr unsigned int apiInvalidVersion = 0;
|
|
||||||
constexpr unsigned int apiVersionIfUnspecified = 1;
|
|
||||||
constexpr unsigned int apiMinimumSupportedVersion = 1;
|
|
||||||
constexpr unsigned int apiMaximumSupportedVersion = 2;
|
|
||||||
constexpr unsigned int apiCommandLineVersion = 1; // TODO Bump to 2 later
|
|
||||||
constexpr unsigned int apiBetaVersion = 3;
|
|
||||||
constexpr unsigned int apiMaximumValidVersion = apiBetaVersion;
|
|
||||||
|
|
||||||
static_assert(apiMinimumSupportedVersion >= apiVersionIfUnspecified);
|
|
||||||
static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion);
|
|
||||||
static_assert(apiBetaVersion >= apiMaximumSupportedVersion);
|
|
||||||
static_assert(apiMaximumValidVersion >= apiMaximumSupportedVersion);
|
|
||||||
|
|
||||||
template <class Object>
|
template <class Object>
|
||||||
void
|
void
|
||||||
setVersion(Object& parent, unsigned int apiVersion, bool betaEnabled)
|
setVersion(Object& parent, unsigned int apiVersion, bool betaEnabled)
|
||||||
@@ -256,7 +223,7 @@ setVersion(Object& parent, unsigned int apiVersion, bool betaEnabled)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
object[jss::first] = apiMinimumSupportedVersion;
|
object[jss::first] = apiMinimumSupportedVersion.value;
|
||||||
object[jss::last] =
|
object[jss::last] =
|
||||||
betaEnabled ? apiBetaVersion : apiMaximumSupportedVersion;
|
betaEnabled ? apiBetaVersion : apiMaximumSupportedVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -643,7 +643,7 @@ ServerHandler::processRequest(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto apiVersion = RPC::apiVersionIfUnspecified;
|
unsigned apiVersion = RPC::apiVersionIfUnspecified;
|
||||||
if (jsonRPC.isMember(jss::params) && jsonRPC[jss::params].isArray() &&
|
if (jsonRPC.isMember(jss::params) && jsonRPC[jss::params].isArray() &&
|
||||||
jsonRPC[jss::params].size() > 0 &&
|
jsonRPC[jss::params].size() > 0 &&
|
||||||
jsonRPC[jss::params][0u].isObject())
|
jsonRPC[jss::params][0u].isObject())
|
||||||
|
|||||||
@@ -1088,10 +1088,7 @@ struct PayChan_test : public beast::unit_test::suite
|
|||||||
args[jss::amount] = 51110000;
|
args[jss::amount] = 51110000;
|
||||||
|
|
||||||
// test for all api versions
|
// test for all api versions
|
||||||
for (auto apiVersion = RPC::apiMinimumSupportedVersion;
|
forAllApiVersions([&, this](unsigned apiVersion) {
|
||||||
apiVersion <= RPC::apiBetaVersion;
|
|
||||||
++apiVersion)
|
|
||||||
{
|
|
||||||
testcase(
|
testcase(
|
||||||
"PayChan Channel_Auth RPC Api " + std::to_string(apiVersion));
|
"PayChan Channel_Auth RPC Api " + std::to_string(apiVersion));
|
||||||
args[jss::api_version] = apiVersion;
|
args[jss::api_version] = apiVersion;
|
||||||
@@ -1101,7 +1098,7 @@ struct PayChan_test : public beast::unit_test::suite
|
|||||||
args.toStyledString())[jss::result];
|
args.toStyledString())[jss::result];
|
||||||
auto const error = apiVersion < 2u ? "invalidParams" : "badKeyType";
|
auto const error = apiVersion < 2u ? "invalidParams" : "badKeyType";
|
||||||
BEAST_EXPECT(rs[jss::error] == error);
|
BEAST_EXPECT(rs[jss::error] == error);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -1,387 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of rippled: https://github.com/XRPLF/rippled/
|
|
||||||
Copyright (c) 2023 Ripple Labs Inc.
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or 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 <ripple/json/MultivarJson.h>
|
|
||||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
|
||||||
|
|
||||||
#include <ripple/beast/unit_test.h>
|
|
||||||
#include "ripple/beast/unit_test/suite.hpp"
|
|
||||||
#include "ripple/json/json_value.h"
|
|
||||||
#include <cstdint>
|
|
||||||
#include <limits>
|
|
||||||
#include <optional>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace ripple {
|
|
||||||
namespace test {
|
|
||||||
|
|
||||||
struct MultivarJson_test : beast::unit_test::suite
|
|
||||||
{
|
|
||||||
static auto
|
|
||||||
makeJson(const char* key, int val)
|
|
||||||
{
|
|
||||||
Json::Value obj1(Json::objectValue);
|
|
||||||
obj1[key] = val;
|
|
||||||
return obj1;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
run() override
|
|
||||||
{
|
|
||||||
Json::Value const obj1 = makeJson("value", 1);
|
|
||||||
Json::Value const obj2 = makeJson("value", 2);
|
|
||||||
Json::Value const obj3 = makeJson("value", 3);
|
|
||||||
Json::Value const jsonNull{};
|
|
||||||
|
|
||||||
MultivarJson<3> subject{};
|
|
||||||
static_assert(sizeof(subject) == sizeof(subject.val));
|
|
||||||
static_assert(subject.size == subject.val.size());
|
|
||||||
static_assert(
|
|
||||||
std::is_same_v<decltype(subject.val), std::array<Json::Value, 3>>);
|
|
||||||
|
|
||||||
BEAST_EXPECT(subject.val.size() == 3);
|
|
||||||
BEAST_EXPECT(
|
|
||||||
(subject.val ==
|
|
||||||
std::array<Json::Value, 3>{jsonNull, jsonNull, jsonNull}));
|
|
||||||
|
|
||||||
subject.val[0] = obj1;
|
|
||||||
subject.val[1] = obj2;
|
|
||||||
|
|
||||||
{
|
|
||||||
testcase("default copy construction / assignment");
|
|
||||||
|
|
||||||
MultivarJson<3> x{subject};
|
|
||||||
|
|
||||||
BEAST_EXPECT(x.val.size() == subject.val.size());
|
|
||||||
BEAST_EXPECT(x.val[0] == subject.val[0]);
|
|
||||||
BEAST_EXPECT(x.val[1] == subject.val[1]);
|
|
||||||
BEAST_EXPECT(x.val[2] == subject.val[2]);
|
|
||||||
BEAST_EXPECT(x.val == subject.val);
|
|
||||||
BEAST_EXPECT(&x.val[0] != &subject.val[0]);
|
|
||||||
BEAST_EXPECT(&x.val[1] != &subject.val[1]);
|
|
||||||
BEAST_EXPECT(&x.val[2] != &subject.val[2]);
|
|
||||||
|
|
||||||
MultivarJson<3> y;
|
|
||||||
BEAST_EXPECT((y.val == std::array<Json::Value, 3>{}));
|
|
||||||
y = subject;
|
|
||||||
BEAST_EXPECT(y.val == subject.val);
|
|
||||||
BEAST_EXPECT(&y.val[0] != &subject.val[0]);
|
|
||||||
BEAST_EXPECT(&y.val[1] != &subject.val[1]);
|
|
||||||
BEAST_EXPECT(&y.val[2] != &subject.val[2]);
|
|
||||||
|
|
||||||
y = std::move(x);
|
|
||||||
BEAST_EXPECT(y.val == subject.val);
|
|
||||||
BEAST_EXPECT(&y.val[0] != &subject.val[0]);
|
|
||||||
BEAST_EXPECT(&y.val[1] != &subject.val[1]);
|
|
||||||
BEAST_EXPECT(&y.val[2] != &subject.val[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
testcase("select");
|
|
||||||
|
|
||||||
BEAST_EXPECT(
|
|
||||||
subject.select([]() -> std::size_t { return 0; }) == obj1);
|
|
||||||
BEAST_EXPECT(
|
|
||||||
subject.select([]() -> std::size_t { return 1; }) == obj2);
|
|
||||||
BEAST_EXPECT(
|
|
||||||
subject.select([]() -> std::size_t { return 2; }) == jsonNull);
|
|
||||||
|
|
||||||
// Tests of requires clause - these are expected to match
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.select([]() -> std::size_t { return 0; });
|
|
||||||
};
|
|
||||||
}(subject));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.select([]() constexpr->std::size_t { return 0; });
|
|
||||||
};
|
|
||||||
}(subject));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.select([]() mutable -> std::size_t { return 0; });
|
|
||||||
};
|
|
||||||
}(subject));
|
|
||||||
|
|
||||||
// Tests of requires clause - these are expected NOT to match
|
|
||||||
static_assert([](auto&& a) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
subject.select([]() -> int { return 0; });
|
|
||||||
};
|
|
||||||
}(subject));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
v.select([]() -> void {});
|
|
||||||
};
|
|
||||||
}(subject));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
v.select([]() -> bool { return false; });
|
|
||||||
};
|
|
||||||
}(subject));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
testcase("set");
|
|
||||||
|
|
||||||
auto x = MultivarJson<2>{Json::objectValue};
|
|
||||||
x.set("name1", 42);
|
|
||||||
BEAST_EXPECT(x.val[0].isMember("name1"));
|
|
||||||
BEAST_EXPECT(x.val[1].isMember("name1"));
|
|
||||||
BEAST_EXPECT(x.val[0]["name1"].isInt());
|
|
||||||
BEAST_EXPECT(x.val[1]["name1"].isInt());
|
|
||||||
BEAST_EXPECT(x.val[0]["name1"].asInt() == 42);
|
|
||||||
BEAST_EXPECT(x.val[1]["name1"].asInt() == 42);
|
|
||||||
|
|
||||||
x.set("name2", "bar");
|
|
||||||
BEAST_EXPECT(x.val[0].isMember("name2"));
|
|
||||||
BEAST_EXPECT(x.val[1].isMember("name2"));
|
|
||||||
BEAST_EXPECT(x.val[0]["name2"].isString());
|
|
||||||
BEAST_EXPECT(x.val[1]["name2"].isString());
|
|
||||||
BEAST_EXPECT(x.val[0]["name2"].asString() == "bar");
|
|
||||||
BEAST_EXPECT(x.val[1]["name2"].asString() == "bar");
|
|
||||||
|
|
||||||
// Tests of requires clause - these are expected to match
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.set("name", Json::nullValue);
|
|
||||||
};
|
|
||||||
}(x));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.set("name", "value");
|
|
||||||
};
|
|
||||||
}(x));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.set("name", true);
|
|
||||||
};
|
|
||||||
}(x));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.set("name", 42);
|
|
||||||
};
|
|
||||||
}(x));
|
|
||||||
|
|
||||||
// Tests of requires clause - these are expected NOT to match
|
|
||||||
struct foo_t final
|
|
||||||
{
|
|
||||||
};
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
v.set("name", foo_t{});
|
|
||||||
};
|
|
||||||
}(x));
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
v.set("name", std::nullopt);
|
|
||||||
};
|
|
||||||
}(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
testcase("isMember");
|
|
||||||
|
|
||||||
// Well defined behaviour even if we have different types of members
|
|
||||||
BEAST_EXPECT(subject.isMember("foo") == decltype(subject)::none);
|
|
||||||
|
|
||||||
{
|
|
||||||
// All variants have element "One", none have element "Two"
|
|
||||||
MultivarJson<2> s1{};
|
|
||||||
s1.val[0] = makeJson("One", 12);
|
|
||||||
s1.val[1] = makeJson("One", 42);
|
|
||||||
BEAST_EXPECT(s1.isMember("One") == decltype(s1)::all);
|
|
||||||
BEAST_EXPECT(s1.isMember("Two") == decltype(s1)::none);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Some variants have element "One" and some have "Two"
|
|
||||||
MultivarJson<2> s2{};
|
|
||||||
s2.val[0] = makeJson("One", 12);
|
|
||||||
s2.val[1] = makeJson("Two", 42);
|
|
||||||
BEAST_EXPECT(s2.isMember("One") == decltype(s2)::some);
|
|
||||||
BEAST_EXPECT(s2.isMember("Two") == decltype(s2)::some);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Not all variants have element "One", because last one is null
|
|
||||||
MultivarJson<3> s3{};
|
|
||||||
s3.val[0] = makeJson("One", 12);
|
|
||||||
s3.val[1] = makeJson("One", 42);
|
|
||||||
BEAST_EXPECT(s3.isMember("One") == decltype(s3)::some);
|
|
||||||
BEAST_EXPECT(s3.isMember("Two") == decltype(s3)::none);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// NOTE It's fine to change this test when we change API versions
|
|
||||||
testcase("apiVersionSelector");
|
|
||||||
|
|
||||||
static_assert(MultiApiJson::size == 3);
|
|
||||||
static MultiApiJson x{obj1};
|
|
||||||
x.val[1] = obj2;
|
|
||||||
x.val[2] = obj3;
|
|
||||||
|
|
||||||
static_assert(
|
|
||||||
std::is_same_v<decltype(apiVersionSelector(1)()), std::size_t>);
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
v.select(apiVersionSelector(1));
|
|
||||||
};
|
|
||||||
}(x));
|
|
||||||
|
|
||||||
BEAST_EXPECT(x.select(apiVersionSelector(0)) == obj1);
|
|
||||||
BEAST_EXPECT(x.select(apiVersionSelector(2)) == obj2);
|
|
||||||
|
|
||||||
static_assert(apiVersionSelector(0)() == 0);
|
|
||||||
static_assert(apiVersionSelector(1)() == 0);
|
|
||||||
static_assert(apiVersionSelector(2)() == 1);
|
|
||||||
static_assert(apiVersionSelector(3)() == 2);
|
|
||||||
static_assert(apiVersionSelector(4)() == 2);
|
|
||||||
static_assert(
|
|
||||||
apiVersionSelector(
|
|
||||||
std::numeric_limits<unsigned int>::max())() == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// There should be no reson to change this test
|
|
||||||
testcase("apiVersionSelector invariants");
|
|
||||||
|
|
||||||
static_assert(
|
|
||||||
apiVersionSelector(RPC::apiMinimumSupportedVersion)() == 0);
|
|
||||||
static_assert(
|
|
||||||
apiVersionSelector(RPC::apiBetaVersion)() + 1 //
|
|
||||||
== MultiApiJson::size);
|
|
||||||
|
|
||||||
BEAST_EXPECT(MultiApiJson::size >= 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
testcase("visit");
|
|
||||||
|
|
||||||
MultivarJson<3> s1{};
|
|
||||||
s1.val[0] = makeJson("value", 2);
|
|
||||||
s1.val[1] = makeJson("value", 3);
|
|
||||||
s1.val[2] = makeJson("value", 5);
|
|
||||||
|
|
||||||
int result = 1;
|
|
||||||
ripple::visit<1, 3>(
|
|
||||||
s1, [&](Json::Value& json, unsigned int i) -> void {
|
|
||||||
if (BEAST_EXPECT(json.isObject() && json.isMember("value")))
|
|
||||||
{
|
|
||||||
auto const value = json["value"].asInt();
|
|
||||||
BEAST_EXPECT(
|
|
||||||
(value == 2 && i == 1) || //
|
|
||||||
(value == 3 && i == 2) || //
|
|
||||||
(value == 5 && i == 3));
|
|
||||||
result *= value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
BEAST_EXPECT(result == 30);
|
|
||||||
|
|
||||||
// Can use fn with constexpr functor
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
ripple::visit<1, 3>(
|
|
||||||
v, [](Json::Value&, unsigned int) constexpr {});
|
|
||||||
};
|
|
||||||
}(s1));
|
|
||||||
|
|
||||||
// Can use fn with deduction over all parameters
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
ripple::visit<1, 3>(v, [](auto&, auto) constexpr {});
|
|
||||||
};
|
|
||||||
}(s1));
|
|
||||||
|
|
||||||
// Can use fn with conversion of version parameter
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return requires
|
|
||||||
{
|
|
||||||
ripple::visit<1, 3>(v, [](auto&, std::size_t) constexpr {});
|
|
||||||
};
|
|
||||||
}(s1));
|
|
||||||
|
|
||||||
// Cannot use fn with const parameter
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
ripple::visit<1, 3>(
|
|
||||||
v, [](Json::Value const&, auto) constexpr {});
|
|
||||||
};
|
|
||||||
}(const_cast<MultivarJson<3> const&>(s1)));
|
|
||||||
|
|
||||||
// Cannot call visit with size mismatch
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
ripple::visit<1, 2>(
|
|
||||||
v, [](Json::Value&, unsigned int) constexpr {});
|
|
||||||
};
|
|
||||||
}(s1));
|
|
||||||
|
|
||||||
// Cannot call visit with version offset
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
ripple::visit<0, 2>(
|
|
||||||
v, [](Json::Value&, unsigned int) constexpr {});
|
|
||||||
};
|
|
||||||
}(s1));
|
|
||||||
|
|
||||||
// Cannot call visit with size mismatch
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
ripple::visit<1, 4>(
|
|
||||||
v, [](Json::Value&, unsigned int) constexpr {});
|
|
||||||
};
|
|
||||||
}(s1));
|
|
||||||
|
|
||||||
// Cannot call visit with wrong order of versions
|
|
||||||
static_assert([](auto&& v) {
|
|
||||||
return !requires
|
|
||||||
{
|
|
||||||
ripple::visit<3, 1>(
|
|
||||||
v, [](Json::Value&, unsigned int) constexpr {});
|
|
||||||
};
|
|
||||||
}(s1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(MultivarJson, ripple_basics, ripple);
|
|
||||||
|
|
||||||
} // namespace test
|
|
||||||
} // namespace ripple
|
|
||||||
@@ -758,40 +758,6 @@ Env::rpc(std::string const& cmd, Args&&... args)
|
|||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The SingleVersionedTestCallable concept checks for a callable that takes
|
|
||||||
* an unsigned integer as its argument and returns void.
|
|
||||||
*/
|
|
||||||
template <class T>
|
|
||||||
concept SingleVersionedTestCallable = requires(T callable, unsigned int version)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
callable(version)
|
|
||||||
}
|
|
||||||
->std::same_as<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The VersionedTestCallable concept checks if a set of callables all satisfy
|
|
||||||
* the SingleVersionedTestCallable concept. This allows forAllApiVersions to
|
|
||||||
* accept any number of functions. It executes a set of provided functions over
|
|
||||||
* a range of versions from RPC::apiMinimumSupportedVersion to
|
|
||||||
* RPC::apiBetaVersion. This is useful for running a series of tests or
|
|
||||||
* operations that need to be performed on multiple versions of an API.
|
|
||||||
*/
|
|
||||||
template <class... T>
|
|
||||||
concept VersionedTestCallable = (... && SingleVersionedTestCallable<T>);
|
|
||||||
void
|
|
||||||
forAllApiVersions(VersionedTestCallable auto... testCallable)
|
|
||||||
{
|
|
||||||
for (auto testVersion = RPC::apiMinimumSupportedVersion;
|
|
||||||
testVersion <= RPC::apiMaximumValidVersion;
|
|
||||||
++testVersion)
|
|
||||||
{
|
|
||||||
(..., testCallable(testVersion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jtx
|
} // namespace jtx
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
75
src/test/protocol/ApiVersion_test.cpp
Normal file
75
src/test/protocol/ApiVersion_test.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/XRPLF/rippled/
|
||||||
|
Copyright (c) 2023 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or 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 <ripple/beast/unit_test.h>
|
||||||
|
#include <ripple/beast/unit_test/suite.hpp>
|
||||||
|
#include <ripple/json/json_value.h>
|
||||||
|
#include <ripple/protocol/ApiVersion.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <optional>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
struct ApiVersion_test : beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
{
|
||||||
|
testcase("API versions invariants");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
RPC::apiMinimumSupportedVersion <=
|
||||||
|
RPC::apiMaximumSupportedVersion);
|
||||||
|
static_assert(
|
||||||
|
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumValidVersion);
|
||||||
|
static_assert(
|
||||||
|
RPC::apiMaximumSupportedVersion <= RPC::apiMaximumValidVersion);
|
||||||
|
static_assert(RPC::apiBetaVersion <= RPC::apiMaximumValidVersion);
|
||||||
|
|
||||||
|
BEAST_EXPECT(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Update when we change versions
|
||||||
|
testcase("API versions");
|
||||||
|
|
||||||
|
static_assert(RPC::apiMinimumSupportedVersion >= 1);
|
||||||
|
static_assert(RPC::apiMinimumSupportedVersion < 2);
|
||||||
|
static_assert(RPC::apiMaximumSupportedVersion >= 2);
|
||||||
|
static_assert(RPC::apiMaximumSupportedVersion < 3);
|
||||||
|
static_assert(RPC::apiMaximumValidVersion >= 3);
|
||||||
|
static_assert(RPC::apiMaximumValidVersion < 4);
|
||||||
|
static_assert(RPC::apiBetaVersion >= 3);
|
||||||
|
static_assert(RPC::apiBetaVersion < 4);
|
||||||
|
|
||||||
|
BEAST_EXPECT(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(ApiVersion, protocol, ripple);
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace ripple
|
||||||
1202
src/test/protocol/MultiApiJson_test.cpp
Normal file
1202
src/test/protocol/MultiApiJson_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -734,7 +734,7 @@ public:
|
|||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
test::jtx::forAllApiVersions(
|
forAllApiVersions(
|
||||||
std::bind_front(&AccountTx_test::testParameters, this));
|
std::bind_front(&AccountTx_test::testParameters, this));
|
||||||
testContents();
|
testContents();
|
||||||
testAccountDelete();
|
testAccountDelete();
|
||||||
|
|||||||
@@ -172,10 +172,7 @@ public:
|
|||||||
qry2[jss::account] = alice.human();
|
qry2[jss::account] = alice.human();
|
||||||
qry2[jss::hotwallet] = "asdf";
|
qry2[jss::hotwallet] = "asdf";
|
||||||
|
|
||||||
for (auto apiVersion = RPC::apiMinimumSupportedVersion;
|
forAllApiVersions([&, this](unsigned apiVersion) {
|
||||||
apiVersion <= RPC::apiBetaVersion;
|
|
||||||
++apiVersion)
|
|
||||||
{
|
|
||||||
qry2[jss::api_version] = apiVersion;
|
qry2[jss::api_version] = apiVersion;
|
||||||
auto jv = wsc->invoke("gateway_balances", qry2);
|
auto jv = wsc->invoke("gateway_balances", qry2);
|
||||||
expect(jv[jss::status] == "error");
|
expect(jv[jss::status] == "error");
|
||||||
@@ -184,7 +181,7 @@ public:
|
|||||||
auto const error =
|
auto const error =
|
||||||
apiVersion < 2u ? "invalidHotWallet" : "invalidParams";
|
apiVersion < 2u ? "invalidHotWallet" : "invalidParams";
|
||||||
BEAST_EXPECT(response[jss::error] == error);
|
BEAST_EXPECT(response[jss::error] == error);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -2305,7 +2305,7 @@ public:
|
|||||||
testLedgerAccountsOption();
|
testLedgerAccountsOption();
|
||||||
testLedgerEntryDID();
|
testLedgerEntryDID();
|
||||||
|
|
||||||
test::jtx::forAllApiVersions(std::bind_front(
|
forAllApiVersions(std::bind_front(
|
||||||
&LedgerRPC_test::testLedgerEntryInvalidParams, this));
|
&LedgerRPC_test::testLedgerEntryInvalidParams, this));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ public:
|
|||||||
{
|
{
|
||||||
testLedgerRequest();
|
testLedgerRequest();
|
||||||
testEvolution();
|
testEvolution();
|
||||||
test::jtx::forAllApiVersions(
|
forAllApiVersions(
|
||||||
std::bind_front(&LedgerRequestRPC_test::testBadInput, this));
|
std::bind_front(&LedgerRequestRPC_test::testBadInput, this));
|
||||||
testMoreThan256Closed();
|
testMoreThan256Closed();
|
||||||
testNonAdmin();
|
testNonAdmin();
|
||||||
|
|||||||
@@ -284,12 +284,9 @@ public:
|
|||||||
testSetAndClear();
|
testSetAndClear();
|
||||||
|
|
||||||
auto withFeatsTests = [this](FeatureBitset features) {
|
auto withFeatsTests = [this](FeatureBitset features) {
|
||||||
for (auto testVersion = RPC::apiMinimumSupportedVersion;
|
forAllApiVersions([&, this](unsigned testVersion) {
|
||||||
testVersion <= RPC::apiBetaVersion;
|
|
||||||
++testVersion)
|
|
||||||
{
|
|
||||||
testDefaultRipple(features, testVersion);
|
testDefaultRipple(features, testVersion);
|
||||||
}
|
});
|
||||||
testNegativeBalance(features);
|
testNegativeBalance(features);
|
||||||
testPairwise(features);
|
testPairwise(features);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6219,8 +6219,7 @@ public:
|
|||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
test::jtx::forAllApiVersions(
|
forAllApiVersions(std::bind_front(&RPCCall_test::testRPCCall, this));
|
||||||
std::bind_front(&RPCCall_test::testRPCCall, this));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -388,7 +388,7 @@ public:
|
|||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testBadInput();
|
testBadInput();
|
||||||
test::jtx::forAllApiVersions(
|
forAllApiVersions(
|
||||||
std::bind_front(&TransactionEntry_test::testRequest, this));
|
std::bind_front(&TransactionEntry_test::testRequest, this));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -849,7 +849,7 @@ public:
|
|||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
using namespace test::jtx;
|
using namespace test::jtx;
|
||||||
test::jtx::forAllApiVersions(
|
forAllApiVersions(
|
||||||
std::bind_front(&Transaction_test::testBinaryRequest, this));
|
std::bind_front(&Transaction_test::testBinaryRequest, this));
|
||||||
|
|
||||||
FeatureBitset const all{supported_amendments()};
|
FeatureBitset const all{supported_amendments()};
|
||||||
@@ -863,7 +863,7 @@ public:
|
|||||||
testRangeCTIDRequest(features);
|
testRangeCTIDRequest(features);
|
||||||
testCTIDValidation(features);
|
testCTIDValidation(features);
|
||||||
testCTIDRPC(features);
|
testCTIDRPC(features);
|
||||||
test::jtx::forAllApiVersions(
|
forAllApiVersions(
|
||||||
std::bind_front(&Transaction_test::testRequest, this, features));
|
std::bind_front(&Transaction_test::testRequest, this, features));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ class Version_test : public beast::unit_test::suite
|
|||||||
"{\"api_version\": " +
|
"{\"api_version\": " +
|
||||||
std::to_string(
|
std::to_string(
|
||||||
std::max(
|
std::max(
|
||||||
RPC::apiMaximumSupportedVersion, RPC::apiBetaVersion) +
|
RPC::apiMaximumSupportedVersion.value,
|
||||||
|
RPC::apiBetaVersion.value) +
|
||||||
1) +
|
1) +
|
||||||
"}");
|
"}");
|
||||||
BEAST_EXPECT(badVersion(re));
|
BEAST_EXPECT(badVersion(re));
|
||||||
@@ -112,15 +113,15 @@ class Version_test : public beast::unit_test::suite
|
|||||||
Json::Value j_object = Json::Value(Json::objectValue);
|
Json::Value j_object = Json::Value(Json::objectValue);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, false) == versionIfUnspecified);
|
RPC::getAPIVersionNumber(j_object, false) == versionIfUnspecified);
|
||||||
j_object[jss::api_version] = RPC::apiVersionIfUnspecified;
|
j_object[jss::api_version] = RPC::apiVersionIfUnspecified.value;
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, false) == versionIfUnspecified);
|
RPC::getAPIVersionNumber(j_object, false) == versionIfUnspecified);
|
||||||
|
|
||||||
j_object[jss::api_version] = RPC::apiMinimumSupportedVersion;
|
j_object[jss::api_version] = RPC::apiMinimumSupportedVersion.value;
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, false) ==
|
RPC::getAPIVersionNumber(j_object, false) ==
|
||||||
RPC::apiMinimumSupportedVersion);
|
RPC::apiMinimumSupportedVersion);
|
||||||
j_object[jss::api_version] = RPC::apiMaximumSupportedVersion;
|
j_object[jss::api_version] = RPC::apiMaximumSupportedVersion.value;
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, false) ==
|
RPC::getAPIVersionNumber(j_object, false) ==
|
||||||
RPC::apiMaximumSupportedVersion);
|
RPC::apiMaximumSupportedVersion);
|
||||||
@@ -133,14 +134,14 @@ class Version_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, false) ==
|
RPC::getAPIVersionNumber(j_object, false) ==
|
||||||
RPC::apiInvalidVersion);
|
RPC::apiInvalidVersion);
|
||||||
j_object[jss::api_version] = RPC::apiBetaVersion;
|
j_object[jss::api_version] = RPC::apiBetaVersion.value;
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, true) == RPC::apiBetaVersion);
|
RPC::getAPIVersionNumber(j_object, true) == RPC::apiBetaVersion);
|
||||||
j_object[jss::api_version] = RPC::apiBetaVersion + 1;
|
j_object[jss::api_version] = RPC::apiBetaVersion + 1;
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, true) == RPC::apiInvalidVersion);
|
RPC::getAPIVersionNumber(j_object, true) == RPC::apiInvalidVersion);
|
||||||
|
|
||||||
j_object[jss::api_version] = RPC::apiInvalidVersion;
|
j_object[jss::api_version] = RPC::apiInvalidVersion.value;
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
RPC::getAPIVersionNumber(j_object, false) ==
|
RPC::getAPIVersionNumber(j_object, false) ==
|
||||||
RPC::apiInvalidVersion);
|
RPC::apiInvalidVersion);
|
||||||
@@ -202,17 +203,17 @@ class Version_test : public beast::unit_test::suite
|
|||||||
"\"id\": 5, "
|
"\"id\": 5, "
|
||||||
"\"method\": \"version\", "
|
"\"method\": \"version\", "
|
||||||
"\"params\": {}}";
|
"\"params\": {}}";
|
||||||
auto const with_wrong_api_verion =
|
auto const with_wrong_api_verion = std::string("{ ") +
|
||||||
std::string("{ ") +
|
|
||||||
"\"jsonrpc\": \"2.0\", "
|
"\"jsonrpc\": \"2.0\", "
|
||||||
"\"ripplerpc\": \"2.0\", "
|
"\"ripplerpc\": \"2.0\", "
|
||||||
"\"id\": 6, "
|
"\"id\": 6, "
|
||||||
"\"method\": \"version\", "
|
"\"method\": \"version\", "
|
||||||
"\"params\": { "
|
"\"params\": { "
|
||||||
"\"api_version\": " +
|
"\"api_version\": " +
|
||||||
std::to_string(
|
std::to_string(std::max(
|
||||||
std::max(RPC::apiMaximumSupportedVersion, RPC::apiBetaVersion) +
|
RPC::apiMaximumSupportedVersion.value,
|
||||||
1) +
|
RPC::apiBetaVersion.value) +
|
||||||
|
1) +
|
||||||
"}}";
|
"}}";
|
||||||
auto re = env.rpc(
|
auto re = env.rpc(
|
||||||
"json2",
|
"json2",
|
||||||
@@ -275,8 +276,9 @@ class Version_test : public beast::unit_test::suite
|
|||||||
jrr[jss::version].isMember(jss::last))
|
jrr[jss::version].isMember(jss::last))
|
||||||
return;
|
return;
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
jrr[jss::version][jss::first] == RPC::apiMinimumSupportedVersion);
|
jrr[jss::version][jss::first] ==
|
||||||
BEAST_EXPECT(jrr[jss::version][jss::last] == RPC::apiBetaVersion);
|
RPC::apiMinimumSupportedVersion.value);
|
||||||
|
BEAST_EXPECT(jrr[jss::version][jss::last] == RPC::apiBetaVersion.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user