mirror of
https://github.com/Xahau/xahaud.git
synced 2026-08-23 00:10:54 +00:00
APIv2: remove tx_history and ledger_header (#4759)
Remove `tx_history` and `ledger_header` methods from API version 2. Update `RPC::Handler` to allow for methods (or method implementations) to be API version specific. This partially resolves #4727. We can now store multiple handlers with the same name, as long as they belong to different (non-overlapping) API versions. This necessarily impacts the handler lookup algorithm and its complexity; however, there is no performance loss on x86_64 architecture, and only minimal performance loss on arm64 (around 10ns). This design change gives us extra flexibility evolving the API in the future, including other parts of In API version 2, `tx_history` and `ledger_header` are no longer recognised; if they are called, `rippled` will return error `unknownCmd` Resolve #3638 Resolve #3539
This commit is contained in:
@@ -17,11 +17,14 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/rpc/handlers/Handlers.h>
|
||||
#include <ripple/rpc/handlers/Version.h>
|
||||
#include <ripple/rpc/impl/Handler.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
namespace {
|
||||
@@ -47,6 +50,9 @@ template <class Object, class HandlerImpl>
|
||||
Status
|
||||
handle(JsonContext& context, Object& object)
|
||||
{
|
||||
assert(
|
||||
context.apiVersion >= HandlerImpl::minApiVer &&
|
||||
context.apiVersion <= HandlerImpl::maxApiVer);
|
||||
HandlerImpl handler(context);
|
||||
|
||||
auto status = handler.check();
|
||||
@@ -55,7 +61,20 @@ handle(JsonContext& context, Object& object)
|
||||
else
|
||||
handler.writeResult(object);
|
||||
return status;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename HandlerImpl>
|
||||
Handler
|
||||
handlerFrom()
|
||||
{
|
||||
return {
|
||||
HandlerImpl::name,
|
||||
&handle<Json::Value, HandlerImpl>,
|
||||
HandlerImpl::role,
|
||||
HandlerImpl::condition,
|
||||
HandlerImpl::minApiVer,
|
||||
HandlerImpl::maxApiVer};
|
||||
}
|
||||
|
||||
Handler const handlerArray[]{
|
||||
// Some handlers not specified here are added to the table via addHandler()
|
||||
@@ -110,7 +129,7 @@ Handler const handlerArray[]{
|
||||
NEEDS_CURRENT_LEDGER},
|
||||
{"ledger_data", byRef(&doLedgerData), Role::USER, NO_CONDITION},
|
||||
{"ledger_entry", byRef(&doLedgerEntry), Role::USER, NO_CONDITION},
|
||||
{"ledger_header", byRef(&doLedgerHeader), Role::USER, NO_CONDITION},
|
||||
{"ledger_header", byRef(&doLedgerHeader), Role::USER, NO_CONDITION, 1, 1},
|
||||
{"ledger_request", byRef(&doLedgerRequest), Role::ADMIN, NO_CONDITION},
|
||||
{"log_level", byRef(&doLogLevel), Role::ADMIN, NO_CONDITION},
|
||||
{"logrotate", byRef(&doLogRotate), Role::ADMIN, NO_CONDITION},
|
||||
@@ -158,7 +177,7 @@ Handler const handlerArray[]{
|
||||
{"stop", byRef(&doStop), Role::ADMIN, NO_CONDITION},
|
||||
{"transaction_entry", byRef(&doTransactionEntry), Role::USER, NO_CONDITION},
|
||||
{"tx", byRef(&doTxJson), Role::USER, NEEDS_NETWORK_CONNECTION},
|
||||
{"tx_history", byRef(&doTxHistory), Role::USER, NO_CONDITION},
|
||||
{"tx_history", byRef(&doTxHistory), Role::USER, NO_CONDITION, 1, 1},
|
||||
{"tx_reduce_relay", byRef(&doTxReduceRelay), Role::USER, NO_CONDITION},
|
||||
{"unl_list", byRef(&doUnlList), Role::ADMIN, NO_CONDITION},
|
||||
{"validation_create",
|
||||
@@ -183,14 +202,42 @@ Handler const handlerArray[]{
|
||||
class HandlerTable
|
||||
{
|
||||
private:
|
||||
using handler_table_t = std::multimap<std::string, Handler>;
|
||||
|
||||
// Use with equal_range to enforce that API range of a newly added handler
|
||||
// does not overlap with API range of an existing handler with same name
|
||||
[[nodiscard]] bool
|
||||
overlappingApiVersion(
|
||||
std::pair<handler_table_t::iterator, handler_table_t::iterator> range,
|
||||
unsigned minVer,
|
||||
unsigned maxVer)
|
||||
{
|
||||
assert(minVer <= maxVer);
|
||||
assert(maxVer <= RPC::apiMaximumValidVersion);
|
||||
|
||||
return std::any_of(
|
||||
range.first,
|
||||
range.second, //
|
||||
[minVer, maxVer](auto const& item) {
|
||||
return item.second.minApiVer_ <= maxVer &&
|
||||
item.second.maxApiVer_ >= minVer;
|
||||
});
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
explicit HandlerTable(const Handler (&entries)[N])
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
for (auto const& entry : entries)
|
||||
{
|
||||
auto const& entry = entries[i];
|
||||
assert(table_.find(entry.name_) == table_.end());
|
||||
table_[entry.name_] = entry;
|
||||
if (overlappingApiVersion(
|
||||
table_.equal_range(entry.name_),
|
||||
entry.minApiVer_,
|
||||
entry.maxApiVer_))
|
||||
LogicError(
|
||||
std::string("Handler for ") + entry.name_ +
|
||||
" overlaps with an existing handler");
|
||||
|
||||
table_.insert({entry.name_, entry});
|
||||
}
|
||||
|
||||
// This is where the new-style handlers are added.
|
||||
@@ -206,7 +253,7 @@ public:
|
||||
return handlerTable;
|
||||
}
|
||||
|
||||
Handler const*
|
||||
[[nodiscard]] Handler const*
|
||||
getHandler(unsigned version, bool betaEnabled, std::string const& name)
|
||||
const
|
||||
{
|
||||
@@ -214,36 +261,48 @@ public:
|
||||
version > (betaEnabled ? RPC::apiBetaVersion
|
||||
: RPC::apiMaximumSupportedVersion))
|
||||
return nullptr;
|
||||
auto i = table_.find(name);
|
||||
return i == table_.end() ? nullptr : &i->second;
|
||||
|
||||
auto const range = table_.equal_range(name);
|
||||
auto const i = std::find_if(
|
||||
range.first, range.second, [version](auto const& entry) {
|
||||
return entry.second.minApiVer_ <= version &&
|
||||
version <= entry.second.maxApiVer_;
|
||||
});
|
||||
|
||||
return i == range.second ? nullptr : &i->second;
|
||||
}
|
||||
|
||||
std::vector<char const*>
|
||||
[[nodiscard]] std::set<char const*>
|
||||
getHandlerNames() const
|
||||
{
|
||||
std::vector<char const*> ret;
|
||||
ret.reserve(table_.size());
|
||||
std::set<char const*> ret;
|
||||
for (auto const& i : table_)
|
||||
ret.push_back(i.second.name_);
|
||||
ret.insert(i.second.name_);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, Handler> table_;
|
||||
handler_table_t table_;
|
||||
|
||||
template <class HandlerImpl>
|
||||
void
|
||||
addHandler()
|
||||
{
|
||||
assert(table_.find(HandlerImpl::name()) == table_.end());
|
||||
static_assert(HandlerImpl::minApiVer <= HandlerImpl::maxApiVer);
|
||||
static_assert(HandlerImpl::maxApiVer <= RPC::apiMaximumValidVersion);
|
||||
static_assert(
|
||||
RPC::apiMinimumSupportedVersion <= HandlerImpl::minApiVer);
|
||||
|
||||
Handler h;
|
||||
h.name_ = HandlerImpl::name();
|
||||
h.valueMethod_ = &handle<Json::Value, HandlerImpl>;
|
||||
h.role_ = HandlerImpl::role();
|
||||
h.condition_ = HandlerImpl::condition();
|
||||
if (overlappingApiVersion(
|
||||
table_.equal_range(HandlerImpl::name),
|
||||
HandlerImpl::minApiVer,
|
||||
HandlerImpl::maxApiVer))
|
||||
LogicError(
|
||||
std::string("Handler for ") + HandlerImpl::name +
|
||||
" overlaps with an existing handler");
|
||||
|
||||
table_[HandlerImpl::name()] = h;
|
||||
table_.insert({HandlerImpl::name, handlerFrom<HandlerImpl>()});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -255,11 +314,11 @@ getHandler(unsigned version, bool betaEnabled, std::string const& name)
|
||||
return HandlerTable::instance().getHandler(version, betaEnabled, name);
|
||||
}
|
||||
|
||||
std::vector<char const*>
|
||||
std::set<char const*>
|
||||
getHandlerNames()
|
||||
{
|
||||
return HandlerTable::instance().getHandlerNames();
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
} // namespace ripple
|
||||
|
||||
Reference in New Issue
Block a user