mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Write improved forAllApiVersions used in NetworkOPs (#4833)
This commit is contained in:
@@ -54,8 +54,9 @@ BookListeners::publish(
|
||||
// Only publish jvObj if this is the first occurence
|
||||
if (havePublished.emplace(p->getSeq()).second)
|
||||
{
|
||||
p->send(
|
||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
||||
jvObj.visit(
|
||||
p->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#ifndef 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/protocol/MultiApiJson.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <ripple/app/ledger/AcceptedLedgerTx.h>
|
||||
#include <ripple/app/ledger/BookListeners.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/json/MultivarJson.h>
|
||||
#include <ripple/protocol/MultiApiJson.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
|
||||
@@ -168,9 +168,7 @@ fillJsonTx(
|
||||
if (validated)
|
||||
{
|
||||
auto const seq = fill.ledger.seq();
|
||||
txJson[jss::ledger_index] = (fill.context->apiVersion > 1)
|
||||
? Json::Value(seq)
|
||||
: Json::Value(std::to_string(seq));
|
||||
txJson[jss::ledger_index] = seq;
|
||||
if (fill.closeTime)
|
||||
txJson[jss::close_time_iso] = to_string_iso(*fill.closeTime);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
#include <ripple/consensus/ConsensusParms.h>
|
||||
#include <ripple/crypto/RFC1751.h>
|
||||
#include <ripple/crypto/csprng.h>
|
||||
#include <ripple/json/MultivarJson.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/nodestore/DatabaseShard.h>
|
||||
#include <ripple/overlay/Cluster.h>
|
||||
@@ -60,6 +59,7 @@
|
||||
#include <ripple/overlay/predicates.h>
|
||||
#include <ripple/protocol/BuildInfo.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/MultiApiJson.h>
|
||||
#include <ripple/protocol/RPCErr.h>
|
||||
#include <ripple/protocol/STParsedJSON.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
@@ -68,7 +68,6 @@
|
||||
#include <ripple/rpc/BookChanges.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/rpc/ServerHandler.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <boost/asio/ip/host_name.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
|
||||
// for consumers supporting different API versions
|
||||
MultiApiJson multiObj{jvObj};
|
||||
visit<RPC::apiMinimumSupportedVersion, RPC::apiMaximumValidVersion>(
|
||||
multiObj, //
|
||||
[](Json::Value& jvTx, unsigned int apiVersion) {
|
||||
multiObj.visit(
|
||||
RPC::apiVersion<1>, //
|
||||
[](Json::Value& jvTx) {
|
||||
// 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] =
|
||||
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())
|
||||
{
|
||||
p->send(
|
||||
multiObj.select(apiVersionSelector(p->getApiVersion())),
|
||||
true);
|
||||
multiObj.visit(
|
||||
p->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||
++i;
|
||||
}
|
||||
else
|
||||
@@ -2769,8 +2768,9 @@ NetworkOPsImp::pubProposedTransaction(
|
||||
|
||||
if (p)
|
||||
{
|
||||
p->send(
|
||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
||||
jvObj.visit(
|
||||
p->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||
++it;
|
||||
}
|
||||
else
|
||||
@@ -3167,13 +3167,14 @@ NetworkOPsImp::transJson(
|
||||
|
||||
std::string const hash = to_string(transaction->getTransactionID());
|
||||
MultiApiJson multiObj{jvObj};
|
||||
visit<RPC::apiMinimumSupportedVersion, RPC::apiMaximumValidVersion>(
|
||||
multiObj, //
|
||||
[&](Json::Value& jvTx, unsigned int apiVersion) {
|
||||
forAllApiVersions(
|
||||
multiObj.visit(), //
|
||||
[&]<unsigned Version>(
|
||||
Json::Value& jvTx, std::integral_constant<unsigned, Version>) {
|
||||
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::hash] = hash;
|
||||
@@ -3210,8 +3211,9 @@ NetworkOPsImp::pubValidatedTransaction(
|
||||
|
||||
if (p)
|
||||
{
|
||||
p->send(
|
||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
||||
jvObj.visit(
|
||||
p->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||
++it;
|
||||
}
|
||||
else
|
||||
@@ -3226,8 +3228,9 @@ NetworkOPsImp::pubValidatedTransaction(
|
||||
|
||||
if (p)
|
||||
{
|
||||
p->send(
|
||||
jvObj.select(apiVersionSelector(p->getApiVersion())), true);
|
||||
jvObj.visit(
|
||||
p->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { p->send(jv, true); });
|
||||
++it;
|
||||
}
|
||||
else
|
||||
@@ -3347,9 +3350,9 @@ NetworkOPsImp::pubAccountTransaction(
|
||||
|
||||
for (InfoSub::ref isrListener : notify)
|
||||
{
|
||||
isrListener->send(
|
||||
jvObj.select(apiVersionSelector(isrListener->getApiVersion())),
|
||||
true);
|
||||
jvObj.visit(
|
||||
isrListener->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { isrListener->send(jv, true); });
|
||||
}
|
||||
|
||||
if (last)
|
||||
@@ -3366,9 +3369,9 @@ NetworkOPsImp::pubAccountTransaction(
|
||||
|
||||
jvObj.set(jss::account_history_tx_index, index->forwardTxIndex_++);
|
||||
|
||||
info.sink_->send(
|
||||
jvObj.select(apiVersionSelector(info.sink_->getApiVersion())),
|
||||
true);
|
||||
jvObj.visit(
|
||||
info.sink_->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { info.sink_->send(jv, true); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3426,9 +3429,9 @@ NetworkOPsImp::pubProposedAccountTransaction(
|
||||
MultiApiJson jvObj = transJson(tx, result, false, ledger, std::nullopt);
|
||||
|
||||
for (InfoSub::ref isrListener : notify)
|
||||
isrListener->send(
|
||||
jvObj.select(apiVersionSelector(isrListener->getApiVersion())),
|
||||
true);
|
||||
jvObj.visit(
|
||||
isrListener->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { isrListener->send(jv, true); });
|
||||
|
||||
assert(
|
||||
jvObj.isMember(jss::account_history_tx_stream) ==
|
||||
@@ -3439,9 +3442,9 @@ NetworkOPsImp::pubProposedAccountTransaction(
|
||||
if (index->forwardTxIndex_ == 0 && !index->haveHistorical_)
|
||||
jvObj.set(jss::account_history_tx_first, true);
|
||||
jvObj.set(jss::account_history_tx_index, index->forwardTxIndex_++);
|
||||
info.sink_->send(
|
||||
jvObj.select(apiVersionSelector(info.sink_->getApiVersion())),
|
||||
true);
|
||||
jvObj.visit(
|
||||
info.sink_->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { info.sink_->send(jv, true); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3647,9 +3650,9 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo)
|
||||
bool unsubscribe) -> bool {
|
||||
if (auto sptr = subInfo.sinkWptr_.lock())
|
||||
{
|
||||
sptr->send(
|
||||
jvObj.select(apiVersionSelector(sptr->getApiVersion())),
|
||||
true);
|
||||
jvObj.visit(
|
||||
sptr->getApiVersion(), //
|
||||
[&](Json::Value const& jv) { sptr->send(jv, true); });
|
||||
|
||||
if (unsubscribe)
|
||||
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/proto/org/xrpl/rpc/v1/xrp_ledger.pb.h>
|
||||
#include <ripple/protocol/ApiVersion.h>
|
||||
#include <ripple/protocol/TxMeta.h>
|
||||
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
@@ -30,6 +31,7 @@
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/Status.h>
|
||||
#include <ripple/rpc/impl/Tuning.h>
|
||||
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
@@ -207,41 +209,6 @@ extern beast::SemanticVersion const firstVersion;
|
||||
extern beast::SemanticVersion const goodVersion;
|
||||
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>
|
||||
void
|
||||
setVersion(Object& parent, unsigned int apiVersion, bool betaEnabled)
|
||||
@@ -256,7 +223,7 @@ setVersion(Object& parent, unsigned int apiVersion, bool betaEnabled)
|
||||
}
|
||||
else
|
||||
{
|
||||
object[jss::first] = apiMinimumSupportedVersion;
|
||||
object[jss::first] = apiMinimumSupportedVersion.value;
|
||||
object[jss::last] =
|
||||
betaEnabled ? apiBetaVersion : apiMaximumSupportedVersion;
|
||||
}
|
||||
|
||||
@@ -643,7 +643,7 @@ ServerHandler::processRequest(
|
||||
continue;
|
||||
}
|
||||
|
||||
auto apiVersion = RPC::apiVersionIfUnspecified;
|
||||
unsigned apiVersion = RPC::apiVersionIfUnspecified;
|
||||
if (jsonRPC.isMember(jss::params) && jsonRPC[jss::params].isArray() &&
|
||||
jsonRPC[jss::params].size() > 0 &&
|
||||
jsonRPC[jss::params][0u].isObject())
|
||||
|
||||
Reference in New Issue
Block a user