#include #include #include #include #include #include namespace xrpl { namespace RPC { namespace { /** Adjust an old-style handler to be call-by-reference. */ template Handler::Method byRef(Function const& f) { return [f](JsonContext& context, Json::Value& result) { result = f(context); if (result.type() != Json::objectValue) { // LCOV_EXCL_START UNREACHABLE("xrpl::RPC::byRef : result is object"); result = RPC::makeObjectValue(result); // LCOV_EXCL_STOP } return Status(); }; } template Status handle(JsonContext& context, Object& object) { XRPL_ASSERT( context.apiVersion >= HandlerImpl::minApiVer && context.apiVersion <= HandlerImpl::maxApiVer, "xrpl::RPC::handle : valid API version"); HandlerImpl handler(context); auto status = handler.check(); if (status) { status.inject(object); } else { handler.writeResult(object); } return status; } template Handler handlerFrom() { return { HandlerImpl::name, &handle, HandlerImpl::role, HandlerImpl::condition, HandlerImpl::minApiVer, HandlerImpl::maxApiVer}; } Handler const handlerArray[]{ // Some handlers not specified here are added to the table via addHandler() // Request-response methods {"account_info", byRef(&doAccountInfo), Role::USER, NO_CONDITION}, {"account_currencies", byRef(&doAccountCurrencies), Role::USER, NO_CONDITION}, {"account_lines", byRef(&doAccountLines), Role::USER, NO_CONDITION}, {"account_channels", byRef(&doAccountChannels), Role::USER, NO_CONDITION}, {"account_nfts", byRef(&doAccountNFTs), Role::USER, NO_CONDITION}, {"account_objects", byRef(&doAccountObjects), Role::USER, NO_CONDITION}, {"account_offers", byRef(&doAccountOffers), Role::USER, NO_CONDITION}, {"account_tx", byRef(&doAccountTx), Role::USER, NO_CONDITION}, {"amm_info", byRef(&doAMMInfo), Role::USER, NO_CONDITION}, {"blacklist", byRef(&doBlackList), Role::ADMIN, NO_CONDITION}, {"book_changes", byRef(&doBookChanges), Role::USER, NO_CONDITION}, {"book_offers", byRef(&doBookOffers), Role::USER, NO_CONDITION}, {"can_delete", byRef(&doCanDelete), Role::ADMIN, NO_CONDITION}, {"channel_authorize", byRef(&doChannelAuthorize), Role::USER, NO_CONDITION}, {"channel_verify", byRef(&doChannelVerify), Role::USER, NO_CONDITION}, {"connect", byRef(&doConnect), Role::ADMIN, NO_CONDITION}, {"consensus_info", byRef(&doConsensusInfo), Role::ADMIN, NO_CONDITION}, {"deposit_authorized", byRef(&doDepositAuthorized), Role::USER, NO_CONDITION}, {"feature", byRef(&doFeature), Role::USER, NO_CONDITION}, {"fee", byRef(&doFee), Role::USER, NEEDS_CURRENT_LEDGER}, {"fetch_info", byRef(&doFetchInfo), Role::ADMIN, NO_CONDITION}, {"gateway_balances", byRef(&doGatewayBalances), Role::USER, NO_CONDITION}, {"get_counts", byRef(&doGetCounts), Role::ADMIN, NO_CONDITION}, {"get_aggregate_price", byRef(&doGetAggregatePrice), Role::USER, NO_CONDITION}, {"ledger_accept", byRef(&doLedgerAccept), Role::ADMIN, NEEDS_CURRENT_LEDGER}, {"ledger_cleaner", byRef(&doLedgerCleaner), Role::ADMIN, NEEDS_NETWORK_CONNECTION}, {"ledger_closed", byRef(&doLedgerClosed), Role::USER, NEEDS_CLOSED_LEDGER}, {"ledger_current", byRef(&doLedgerCurrent), Role::USER, 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, 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}, {"manifest", byRef(&doManifest), Role::USER, NO_CONDITION}, {"nft_buy_offers", byRef(&doNFTBuyOffers), Role::USER, NO_CONDITION}, {"nft_sell_offers", byRef(&doNFTSellOffers), Role::USER, NO_CONDITION}, {"noripple_check", byRef(&doNoRippleCheck), Role::USER, NO_CONDITION}, {"owner_info", byRef(&doOwnerInfo), Role::USER, NEEDS_CURRENT_LEDGER}, {"peers", byRef(&doPeers), Role::ADMIN, NO_CONDITION}, {"path_find", byRef(&doPathFind), Role::USER, NEEDS_CURRENT_LEDGER}, {"ping", byRef(&doPing), Role::USER, NO_CONDITION}, {"print", byRef(&doPrint), Role::ADMIN, NO_CONDITION}, // { "profile", byRef (&doProfile), Role::USER, // NEEDS_CURRENT_LEDGER }, {"random", byRef(&doRandom), Role::USER, NO_CONDITION}, {"peer_reservations_add", byRef(&doPeerReservationsAdd), Role::ADMIN, NO_CONDITION}, {"peer_reservations_del", byRef(&doPeerReservationsDel), Role::ADMIN, NO_CONDITION}, {"peer_reservations_list", byRef(&doPeerReservationsList), Role::ADMIN, NO_CONDITION}, {"ripple_path_find", byRef(&doRipplePathFind), Role::USER, NO_CONDITION}, {"server_definitions", byRef(&doServerDefinitions), Role::USER, NO_CONDITION}, {"server_info", byRef(&doServerInfo), Role::USER, NO_CONDITION}, {"server_state", byRef(&doServerState), Role::USER, NO_CONDITION}, {"sign", byRef(&doSign), Role::USER, NO_CONDITION}, {"sign_for", byRef(&doSignFor), Role::USER, NO_CONDITION}, {"simulate", byRef(&doSimulate), Role::USER, NEEDS_CURRENT_LEDGER}, {"stop", byRef(&doStop), Role::ADMIN, NO_CONDITION}, {"submit", byRef(&doSubmit), Role::USER, NEEDS_CURRENT_LEDGER}, {"submit_multisigned", byRef(&doSubmitMultiSigned), Role::USER, NEEDS_CURRENT_LEDGER}, {"transaction_entry", byRef(&doTransactionEntry), Role::USER, NO_CONDITION}, {"tx", byRef(&doTxJson), Role::USER, NEEDS_NETWORK_CONNECTION}, {"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", byRef(&doValidationCreate), Role::ADMIN, NO_CONDITION}, {"validators", byRef(&doValidators), Role::ADMIN, NO_CONDITION}, {"validator_list_sites", byRef(&doValidatorListSites), Role::ADMIN, NO_CONDITION}, {"validator_info", byRef(&doValidatorInfo), Role::ADMIN, NO_CONDITION}, {"vault_info", byRef(&doVaultInfo), Role::USER, NO_CONDITION}, {"wallet_propose", byRef(&doWalletPropose), Role::ADMIN, NO_CONDITION}, // Event methods {"subscribe", byRef(&doSubscribe), Role::USER, NO_CONDITION}, {"unsubscribe", byRef(&doUnsubscribe), Role::USER, NO_CONDITION}, }; class HandlerTable { private: using handler_table_t = std::multimap; // 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]] static bool overlappingApiVersion( std::pair range, unsigned minVer, unsigned maxVer) { XRPL_ASSERT(minVer <= maxVer, "xrpl::RPC::HandlerTable : valid API version range"); XRPL_ASSERT( maxVer <= RPC::apiMaximumValidVersion, "xrpl::RPC::HandlerTable : valid max API version"); return std::any_of( range.first, range.second, // [minVer, maxVer](auto const& item) { return item.second.minApiVer_ <= maxVer && item.second.maxApiVer_ >= minVer; }); } template explicit HandlerTable(Handler const (&entries)[N]) { for (auto const& entry : entries) { 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. addHandler(); addHandler(); } public: static HandlerTable const& instance() { static HandlerTable const handlerTable(handlerArray); return handlerTable; } [[nodiscard]] Handler const* getHandler(unsigned version, bool betaEnabled, std::string const& name) const { if (version < RPC::apiMinimumSupportedVersion || version > (betaEnabled ? RPC::apiBetaVersion : RPC::apiMaximumSupportedVersion)) return nullptr; 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; } [[nodiscard]] std::set getHandlerNames() const { std::set ret; for (auto const& i : table_) ret.insert(i.second.name_); return ret; } private: handler_table_t table_; template void addHandler() { static_assert(HandlerImpl::minApiVer <= HandlerImpl::maxApiVer); static_assert(HandlerImpl::maxApiVer <= RPC::apiMaximumValidVersion); static_assert(RPC::apiMinimumSupportedVersion <= HandlerImpl::minApiVer); if (overlappingApiVersion( table_.equal_range(HandlerImpl::name), HandlerImpl::minApiVer, HandlerImpl::maxApiVer)) { LogicError( std::string("Handler for ") + HandlerImpl::name + " overlaps with an existing handler"); } table_.insert({HandlerImpl::name, handlerFrom()}); } }; } // namespace Handler const* getHandler(unsigned version, bool betaEnabled, std::string const& name) { return HandlerTable::instance().getHandler(version, betaEnabled, name); } std::set getHandlerNames() { return HandlerTable::instance().getHandlerNames(); } } // namespace RPC } // namespace xrpl