diff --git a/API-CHANGELOG.md b/API-CHANGELOG.md index ff0b39c7ac..09803f85b2 100644 --- a/API-CHANGELOG.md +++ b/API-CHANGELOG.md @@ -60,6 +60,7 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta - `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728) - `ledger`: `delivered_amount` is now included in the metadata of successful `AccountDelete` transactions when transactions are expanded (`expand`, or admin-only `full`). Previously it was only added for `Payment` and `CheckCash`, which made `ledger` inconsistent with `tx` and `account_tx`. [#5706](https://github.com/XRPLF/rippled/pull/5706) - `noripple_check`: The `transactions` field is no longer included in error responses; it is still returned (possibly as an empty array) whenever `transactions` is `true` and the request succeeds. A malformed `account` is now rejected before the ledger is looked up, so that error response no longer carries the `ledger_hash`, `ledger_index`, and `validated` fields ([#6303](https://github.com/XRPLF/rippled/pull/6303)). +- `transaction_entry`: An object or an array in `tx_hash` now returns `malformedRequest`, like any other value that is not a hex hash, instead of an `internal` error. ## XRP Ledger server version 3.3.0 diff --git a/cmake/XrplCore.cmake b/cmake/XrplCore.cmake index d7a490be58..a06634eac5 100644 --- a/cmake/XrplCore.cmake +++ b/cmake/XrplCore.cmake @@ -296,7 +296,7 @@ if(xrpld) OUT_VAR rpcspec_instantiations VALUE_TYPE "::json::Value" VIEW_HEADER "xrpld/rpc/detail/JsonObjectView.hpp" - HANDLERS ledger + HANDLERS book_changes ledger transaction_entry ) target_sources(xrpld PRIVATE ${rpcspec_instantiations}) diff --git a/conan.lock b/conan.lock index 5e7be8d6f5..0d130a9c75 100644 --- a/conan.lock +++ b/conan.lock @@ -3,7 +3,7 @@ "requires": [ "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708", "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688", - "xrpl-rpc-spec/0.1.20#6daa13eeb4e6c92b82ea6d78866e1da8%1790720024.368048", + "xrpl-rpc-spec/0.1.21#d536f87a2ae7d313452746cfa3ac4404%1790869005.122975", "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447", "soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521", "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168", diff --git a/conanfile.py b/conanfile.py index 294e036d92..ad8ddc7be3 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class Xrpl(ConanFile): "nudb/2.0.9", "openssl/3.6.3", "soci/4.0.3", - "xrpl-rpc-spec/0.1.20", + "xrpl-rpc-spec/0.1.21", "zlib/1.3.2", ] diff --git a/src/test/rpc/RPCHelpers_test.cpp b/src/test/rpc/RPCHelpers_test.cpp index ac57f4e2ac..fc3e8a4e17 100644 --- a/src/test/rpc/RPCHelpers_test.cpp +++ b/src/test/rpc/RPCHelpers_test.cpp @@ -1,4 +1,3 @@ -#include #include #include @@ -24,49 +23,50 @@ public: // Test no type. json::Value tx = json::ValueType::Object; auto result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status::kOK); + BEAST_EXPECT(result.first == ::rpc::Status::kOK); BEAST_EXPECT(result.second == 0); // Test empty type. tx[jss::type] = ""; result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams}); + BEAST_EXPECT(result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type'.")); BEAST_EXPECT(result.second == 0); // Test type using canonical name in mixedcase. tx[jss::type] = "MPTokenIssuance"; result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status::kOK); + BEAST_EXPECT(result.first == ::rpc::Status::kOK); BEAST_EXPECT(result.second == ltMPTOKEN_ISSUANCE); // Test type using canonical name in lowercase. tx[jss::type] = "mptokenissuance"; result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status::kOK); + BEAST_EXPECT(result.first == ::rpc::Status::kOK); BEAST_EXPECT(result.second == ltMPTOKEN_ISSUANCE); // Test type using RPC name with exact match. tx[jss::type] = "mpt_issuance"; result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status::kOK); + BEAST_EXPECT(result.first == ::rpc::Status::kOK); BEAST_EXPECT(result.second == ltMPTOKEN_ISSUANCE); // Test type using RPC name with inexact match. tx[jss::type] = "MPT_Issuance"; result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams}); + BEAST_EXPECT(result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type'.")); BEAST_EXPECT(result.second == 0); // Test invalid type. tx[jss::type] = 1234; result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams}); + BEAST_EXPECT( + result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type', not string.")); BEAST_EXPECT(result.second == 0); // Test unknown type. tx[jss::type] = "unknown"; result = rpc::chooseLedgerEntryType(tx); - BEAST_EXPECT(result.first == rpc::Status{RpcInvalidParams}); + BEAST_EXPECT(result.first == ::rpc::Status(RpcInvalidParams, "Invalid field 'type'.")); BEAST_EXPECT(result.second == 0); } diff --git a/src/test/rpc/Status_test.cpp b/src/test/rpc/Status_test.cpp deleted file mode 100644 index aaf696e9af..0000000000 --- a/src/test/rpc/Status_test.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace xrpl::rpc { - -class codeString_test : public beast::unit_test::Suite -{ -private: - template - std::string - codeString(Type t) - { - return Status(t).codeString(); - } - - void - testOk() - { - testcase("OK"); - { - auto s = codeString(Status()); - expect(s.empty(), "String for OK status"); - } - - { - auto s = codeString(Status::kOK); - expect(s.empty(), "String for OK status"); - } - - { - auto s = codeString(0); - expect(s.empty(), "String for 0 status"); - } - - { - auto s = codeString(tesSUCCESS); - expect(s.empty(), "String for tesSUCCESS"); - } - - { - auto s = codeString(RpcSuccess); - expect(s.empty(), "String for rpcSUCCESS"); - } - } - - void - testError() - { - testcase("error"); - { - auto s = codeString(23); - expect(s == "23", s); - } - - { - auto s = codeString(temBAD_AMOUNT); - expect(s == "temBAD_AMOUNT: Malformed: Bad amount.", s); - } - - { - auto s = codeString(RpcBadSyntax); - expect(s == "badSyntax: Syntax error.", s); - } - } - -public: - void - run() override - { - testOk(); - testError(); - } -}; - -BEAST_DEFINE_TESTSUITE(codeString, rpc, RPC); - -class fillJson_test : public beast::unit_test::Suite -{ -private: - json::Value value_; - - template - void - fillJson(Type t) - { - value_.clear(); - Status(t).fillJson(value_); - } - - void - testOk() - { - testcase("OK"); - fillJson(Status()); - expect(!value_, "Value for empty status"); - - fillJson(0); - expect(!value_, "Value for 0 status"); - - fillJson(Status::kOK); - expect(!value_, "Value for OK status"); - - fillJson(tesSUCCESS); - expect(!value_, "Value for tesSUCCESS"); - - fillJson(RpcSuccess); - expect(!value_, "Value for rpcSUCCESS"); - } - - template - void - expectFill( - std::string const& label, - Type status, - Status::Strings messages, - std::string const& message) - { - value_.clear(); - fillJson(Status(status, messages)); - - auto prefix = label + ": "; - expect(bool(value_), prefix + "No value"); - - auto error = value_[jss::error]; - expect(bool(error), prefix + "No error."); - - auto code = error[jss::code].asInt(); - expect( - status == code, - prefix + "Wrong status " + std::to_string(code) + " != " + std::to_string(status)); - - auto m = error[jss::message].asString(); - expect(m == message, m + " != " + message); - - auto d = error[jss::data]; - size_t const s1 = d.size(); - size_t const s2 = messages.size(); - expect( - s1 == s2, - prefix + "Data sizes differ " + std::to_string(s1) + " != " + std::to_string(s2)); - for (auto i = 0; i < std::min(s1, s2); ++i) - { - auto ds = d[i].asString(); - expect(ds == messages[i], prefix + ds + " != " + messages[i]); - } - } - - void - testError() - { - testcase("error"); - expectFill("temBAD_AMOUNT", temBAD_AMOUNT, {}, "temBAD_AMOUNT: Malformed: Bad amount."); - - expectFill( - "RpcBadSyntax", - RpcBadSyntax, - {"An error.", "Another error."}, - "badSyntax: Syntax error."); - - expectFill("integer message", 23, {"Stuff."}, "23"); - } - - void - testThrow() - { - testcase("throw"); - try - { - Throw(Status(temBAD_PATH, {"path=sdcdfd"})); - } - catch (Status const& s) - { - expect(s.toTER() == temBAD_PATH, "temBAD_PATH wasn't thrown"); - auto msgs = s.messages(); - expect(msgs.size() == 1, "Wrong number of messages"); - expect(msgs[0] == "path=sdcdfd", msgs[0]); - } - catch (std::exception const&) - { - expect(false, "Didn't catch a Status"); - } - } - -public: - void - run() override - { - testOk(); - testError(); - testThrow(); - } -}; - -BEAST_DEFINE_TESTSUITE(fillJson, rpc, xrpl); - -} // namespace xrpl::rpc diff --git a/src/test/rpc/TransactionEntry_test.cpp b/src/test/rpc/TransactionEntry_test.cpp index b57c615b71..394b152f90 100644 --- a/src/test/rpc/TransactionEntry_test.cpp +++ b/src/test/rpc/TransactionEntry_test.cpp @@ -59,6 +59,19 @@ class TransactionEntry_test : public beast::unit_test::Suite BEAST_EXPECT(result[jss::status] == "error"); } + { + // No ledger selects the current one, not any ledger. + json::Value params{json::ValueType::Object}; + params[jss::tx_hash] = + "E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"; + auto const result = env.client().invoke("transaction_entry", params)[jss::result]; + BEAST_EXPECT(result[jss::error] == "notYetImplemented"); + BEAST_EXPECT(result[jss::status] == "error"); + BEAST_EXPECT(result.isMember(jss::ledger_current_index)); + BEAST_EXPECT(!result.isMember(jss::ledger_hash)); + BEAST_EXPECT(result[jss::validated] == false); + } + { json::Value params{json::ValueType::Object}; params[jss::ledger] = "closed"; @@ -69,6 +82,16 @@ class TransactionEntry_test : public beast::unit_test::Suite BEAST_EXPECT(result[jss::status] == "error"); } + for (auto const type : {json::ValueType::Object, json::ValueType::Array}) + { + json::Value params{json::ValueType::Object}; + params[jss::ledger] = "closed"; + params[jss::tx_hash] = json::Value{type}; + auto const result = env.client().invoke("transaction_entry", params)[jss::result]; + BEAST_EXPECT(result[jss::error] == "malformedRequest"); + BEAST_EXPECT(result[jss::status] == "error"); + } + std::string const txHash{ "E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"}; diff --git a/src/xrpld/rpc/RPCHandler.h b/src/xrpld/rpc/RPCHandler.h index 483e7e5baa..19d2d0f60d 100644 --- a/src/xrpld/rpc/RPCHandler.h +++ b/src/xrpld/rpc/RPCHandler.h @@ -2,10 +2,11 @@ #include #include -#include #include +#include + #include namespace xrpl::rpc { @@ -15,7 +16,7 @@ struct JsonContext; /** * Execute an RPC command and store the results in a json::Value. */ -Status +::rpc::Status doCommand(rpc::JsonContext&, json::Value&); Role diff --git a/src/xrpld/rpc/Status.h b/src/xrpld/rpc/Status.h deleted file mode 100644 index e2716bd579..0000000000 --- a/src/xrpld/rpc/Status.h +++ /dev/null @@ -1,161 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace xrpl::rpc { - -/** - * Status represents the results of an operation that might fail. - * - * It wraps the legacy codes TER and error_code_i, providing both a uniform - * interface and a way to attach additional information to existing status - * returns. - * - * A Status can also be used to fill a json::Value with a JSON-RPC 2.0 - * error response: see http://www.jsonrpc.org/specification#error_object - */ -struct Status : public std::exception -{ -public: - enum class Type { None, TER, ErrorCodeI }; - using Code = int; - using Strings = std::vector; - - static constexpr Code kOK = 0; - - Status() = default; - - // The constraint allows only integers (not enums). Prevents enum narrowing. - template - Status(T code, Strings d = {}) - requires(std::is_integral_v) - : code_(code), messages_(std::move(d)) - { - } - - Status(TER ter, Strings d = {}) - : type_(Type::TER), code_(TERtoInt(ter)), messages_(std::move(d)) - { - } - - Status(ErrorCodeI e, Strings d = {}) - : type_(Type::ErrorCodeI), code_(e), messages_(std::move(d)) - { - } - - Status(ErrorCodeI e, std::string const& s) : type_(Type::ErrorCodeI), code_(e), messages_({s}) - { - } - - /** - * If the Status is OK, the result is an empty string. - * - * @return a representation of the integer status Code as a string. - */ - [[nodiscard]] std::string - codeString() const; - - /** - * Returns true if the Status is *not* OK. - */ - operator bool() const - { - return code_ != kOK; - } - - /** - * Returns true if the Status is OK. - */ - bool - operator!() const - { - return !bool(*this); - } - - /** - * Returns the Status as a TER. - * This may only be called if type() == Type::TER. - */ - [[nodiscard]] TER - toTER() const - { - XRPL_ASSERT(type_ == Type::TER, "xrpl::rpc::Status::toTER : type is TER"); - return TER::fromInt(code_); - } - - /** - * Returns the Status as an error_code_i. - * This may only be called if type() == Type::ErrorCodeI. - */ - [[nodiscard]] ErrorCodeI - toErrorCode() const - { - XRPL_ASSERT( - type_ == Type::ErrorCodeI, "xrpl::rpc::Status::toErrorCode : type is error code"); - return ErrorCodeI(code_); - } - - /** - * Apply the Status to a JsonObject - */ - void - inject(json::Value& object) const - { - if (auto ec = toErrorCode()) - { - if (messages_.empty()) - { - injectError(ec, object); - } - else - { - injectError(ec, message(), object); - } - } - } - - [[nodiscard]] Strings const& - messages() const - { - return messages_; - } - - /** - * Return the first message, if any. - */ - [[nodiscard]] std::string - message() const; - - [[nodiscard]] Type - type() const - { - return type_; - } - - [[nodiscard]] std::string - toString() const; - - /** - * Fill a json::Value with an RPC 2.0 response. - * If the Status is OK, fillJson has no effect. - * Not currently used. - */ - void - fillJson(json::Value&); - -private: - Type type_ = Type::None; - Code code_ = kOK; - Strings messages_; -}; - -} // namespace xrpl::rpc diff --git a/src/xrpld/rpc/detail/Handler.cpp b/src/xrpld/rpc/detail/Handler.cpp index ca4fbe762e..81e0607248 100644 --- a/src/xrpld/rpc/detail/Handler.cpp +++ b/src/xrpld/rpc/detail/Handler.cpp @@ -3,17 +3,17 @@ #include #include #include -#include #include #include #include +#include #include +#include #include #include #include #include -#include #include #include @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -41,7 +40,7 @@ using Method = Handler::Method; * names a plain function instead of returning a closure over it. */ template -Status +::rpc::Status byRef(JsonContext& context, json::Value& result) { result = Function(context); @@ -53,7 +52,7 @@ byRef(JsonContext& context, json::Value& result) // LCOV_EXCL_STOP } - return Status(); + return ::rpc::Status::kOK; } template @@ -63,7 +62,7 @@ template concept InputlessHandler = requires(HandlerImpl& handler) { handler.process(); }; template -Status +::rpc::Status respond( HandlerImpl const& handler, json::Value& object, @@ -73,32 +72,29 @@ respond( if (not output.has_value()) { injectSpecError(object, output.error()); - return std::get(output.error().code); + return output.error(); } - handler.writeResult(object, *output); - injectSpecWarnings(object, warnings); + json::Value result; + handler.writeResult(result, *output); + injectSpecWarnings(result, warnings); + object = std::move(result); - return Status::kOK; + return ::rpc::Status::kOK; } template - requires SpecBasedHandler or InputlessHandler -Status +::rpc::Status handle(JsonContext& context, json::Value& object) + requires SpecBasedHandler or InputlessHandler { - XRPL_ASSERT( - context.apiVersion >= HandlerImpl::minApiVer && - context.apiVersion <= HandlerImpl::maxApiVer, - "xrpl::rpc::handle : valid API version"); - if constexpr (SpecBasedHandler) { auto const input = HandlerImpl::parseInput(context.params, context.apiVersion); if (not input.has_value()) { injectSpecError(object, input.error()); - return std::get(input.error().code); + return input.error(); } HandlerImpl handler(context); @@ -115,28 +111,7 @@ handle(JsonContext& context, json::Value& object) } } -template -constexpr Handler -handlerFrom() -{ - static_assert(HandlerImpl::minApiVer <= HandlerImpl::maxApiVer); - static_assert(HandlerImpl::maxApiVer <= rpc::kApiMaximumValidVersion); - static_assert(rpc::kApiMinimumSupportedVersion <= HandlerImpl::minApiVer); - - return { - HandlerImpl::name, - Method::of<&handle>(), - HandlerImpl::role, - HandlerImpl::condition, - HandlerImpl::minApiVer, - HandlerImpl::maxApiVer, - }; -} - -// The handlers that name the function they dispatch to. The order is free: -// getHandler() searches kHandlers below, which is this array and the next one -// sorted together. -constexpr auto kFunctionHandlerArray = std::to_array({ +constexpr auto kHandlerArray = std::to_array({ // Request-response methods { .name = method::kAccountInfo, @@ -201,7 +176,7 @@ constexpr auto kFunctionHandlerArray = std::to_array({ }, { .name = method::kBookChanges, - .valueMethod = Method::of<&byRef<&doBookChanges>>(), + .valueMethod = Method::of<&handle>(), .role = Role::USER, .condition = Condition::NoCondition, }, @@ -285,6 +260,12 @@ constexpr auto kFunctionHandlerArray = std::to_array({ .condition = Condition::NoCondition, .hasCommandLineForm = false, }, + { + .name = method::kLedger, + .valueMethod = Method::of<&handle>(), + .role = Role::USER, + .condition = Condition::NoCondition, + }, { .name = method::kLedgerAccept, .valueMethod = Method::of<&byRef<&doLedgerAccept>>(), @@ -492,7 +473,7 @@ constexpr auto kFunctionHandlerArray = std::to_array({ }, { .name = method::kTransactionEntry, - .valueMethod = Method::of<&byRef<&doTransactionEntry>>(), + .valueMethod = Method::of<&handle>(), .role = Role::USER, .condition = Condition::NoCondition, }, @@ -555,6 +536,12 @@ constexpr auto kFunctionHandlerArray = std::to_array({ .role = Role::USER, .condition = Condition::NoCondition, }, + { + .name = method::kVersion, + .valueMethod = Method::of<&handle>(), + .role = Role::USER, + .condition = Condition::NoCondition, + }, { .name = method::kWalletPropose, .valueMethod = Method::of<&byRef<&doWalletPropose>>(), @@ -576,39 +563,9 @@ constexpr auto kFunctionHandlerArray = std::to_array({ }, }); -// The class-based handlers, which carry their name and API range as static -// members rather than as a table entry, so they cannot go in the array above. -constexpr auto kClassHandlerArray = std::to_array({ - handlerFrom(), - handlerFrom(), -}); - -/** - * Join the two handler arrays above into one. - * - * Handler has no default constructor, so every entry is built in place from an - * index pack rather than the array being sized and then copied into. The packs - * come from the arrays themselves, so adding a handler to either needs no change - * here. - * - * @return kFunctionHandlerArray followed by kClassHandlerArray. - */ -constexpr auto -joinHandlers() -{ - constexpr auto kFunctionIndices = std::make_index_sequence{}; - constexpr auto kClassIndices = std::make_index_sequence{}; - - return []( - std::index_sequence, std::index_sequence) { - return std::array{ - kFunctionHandlerArray[Function]..., kClassHandlerArray[Class]...}; - }(kFunctionIndices, kClassIndices); -} - // The whole dispatch table. constexpr auto kHandlers = [] { - auto all = joinHandlers(); + auto all = kHandlerArray; // Sorted by name, so a handler can be found by binary search. std::ranges::sort(all, {}, &Handler::name); diff --git a/src/xrpld/rpc/detail/Handler.h b/src/xrpld/rpc/detail/Handler.h index 593eafb4a4..18f72db388 100644 --- a/src/xrpld/rpc/detail/Handler.h +++ b/src/xrpld/rpc/detail/Handler.h @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -13,6 +12,8 @@ #include #include +#include + #include #include @@ -47,7 +48,7 @@ struct Handler class Method { public: - using Function = Status (*)(JsonContext&, json::Value&); + using Function = ::rpc::Status (*)(JsonContext&, json::Value&); /** * Build a Method that calls a given function. @@ -69,7 +70,7 @@ struct Handler * @param result The object the function writes its reply into. * @return The status the function returns. */ - Status + ::rpc::Status operator()(JsonContext& context, json::Value& result) const { return fn_(context, result); diff --git a/src/xrpld/rpc/detail/RPCHandler.cpp b/src/xrpld/rpc/detail/RPCHandler.cpp index 5501cdfde3..893d464beb 100644 --- a/src/xrpld/rpc/detail/RPCHandler.cpp +++ b/src/xrpld/rpc/detail/RPCHandler.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -18,6 +17,8 @@ #include #include +#include + #include #include #include @@ -154,7 +155,7 @@ fillHandler(JsonContext& context, Handler const*& result) return RpcSuccess; } -Status +::rpc::Status callMethod(JsonContext& context, Handler::Method method, std::string_view name, json::Value& result) { static std::atomic kRequestId{0}; @@ -190,7 +191,7 @@ callMethod(JsonContext& context, Handler::Method method, std::string_view name, } // namespace -Status +::rpc::Status doCommand(rpc::JsonContext& context, json::Value& result) { Handler const* handler = nullptr; diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index b35951ffb6..bee9b5bb78 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -12,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -33,6 +31,8 @@ #include +#include + #include #include #include @@ -370,10 +370,10 @@ keypairForSignature(json::Value const& params, json::Value& error, unsigned int return generateKeyPair(*keyType, *seed); } -std::pair +std::pair<::rpc::Status, LedgerEntryType> chooseLedgerEntryType(json::Value const& params) { - std::pair result{rpc::Status::kOK, ltANY}; + std::pair<::rpc::Status, LedgerEntryType> result{::rpc::Status::kOK, ltANY}; if (params.isMember(jss::type)) { static constexpr auto kTypes = @@ -392,10 +392,7 @@ chooseLedgerEntryType(json::Value const& params) auto const& p = params[jss::type]; if (!p.isString()) { - result.first = rpc::Status{RpcInvalidParams, "Invalid field 'type', not string."}; - XRPL_ASSERT( - result.first.type() == rpc::Status::Type::ErrorCodeI, - "xrpl::rpc::chooseLedgerEntryType : first valid result type"); + result.first = ::rpc::Status{RpcInvalidParams, "Invalid field 'type', not string."}; return result; } @@ -408,11 +405,7 @@ chooseLedgerEntryType(json::Value const& params) }); if (iter == kTypes.end()) { - result.first = rpc::Status{RpcInvalidParams, "Invalid field 'type'."}; - XRPL_ASSERT( - result.first.type() == rpc::Status::Type::ErrorCodeI, - "xrpl::rpc::chooseLedgerEntryType : second valid result " - "type"); + result.first = ::rpc::Status{RpcInvalidParams, "Invalid field 'type'."}; return result; } result.second = std::get<2>(*iter); diff --git a/src/xrpld/rpc/detail/RPCHelpers.h b/src/xrpld/rpc/detail/RPCHelpers.h index b25308c0db..3b9deae70a 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.h +++ b/src/xrpld/rpc/detail/RPCHelpers.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -21,6 +20,8 @@ #include #include +#include + #include #include #include @@ -147,7 +148,7 @@ parseXrplLibSeed(json::Value const& params); * @param params The JSON value containing RPC parameters. * @return A pair consisting of the RPC status and the chosen LedgerEntryType. */ -std::pair +std::pair<::rpc::Status, LedgerEntryType> chooseLedgerEntryType(json::Value const& params); /** diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp index cf7ab843cb..aecbabc0eb 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -24,6 +24,7 @@ #include // IWYU pragma: keep #include // IWYU pragma: keep #include +#include #include #include @@ -56,7 +57,7 @@ isValidatedOld(LedgerMaster& ledgerMaster, bool standalone) } template -Status +::rpc::Status ledgerFromHash( T& ledger, json::Value hash, @@ -70,7 +71,7 @@ ledgerFromHash( } template -Status +::rpc::Status ledgerFromIndex( T& ledger, json::Value indexValue, @@ -96,7 +97,7 @@ ledgerFromIndex( } template -Status +::rpc::Status ledgerFromRequest(T& ledger, JsonContext const& context) { ledger.reset(); @@ -163,7 +164,7 @@ ledgerFromRequest(T& ledger, JsonContext const& context) } // namespace template -Status +::rpc::Status ledgerFromRequest(T& ledger, GRPCContext const& context) { R const& request = context.params; @@ -171,25 +172,25 @@ ledgerFromRequest(T& ledger, GRPCContext const& context) } // explicit instantiation of above function -template Status +template ::rpc::Status ledgerFromRequest<>( std::shared_ptr&, GRPCContext const&); // explicit instantiation of above function -template Status +template ::rpc::Status ledgerFromRequest<>( std::shared_ptr&, GRPCContext const&); // explicit instantiation of above function -template Status +template ::rpc::Status ledgerFromRequest<>( std::shared_ptr&, GRPCContext const&); template -Status +::rpc::Status ledgerFromSpecifier( T& ledger, org::xrpl::rpc::v1::LedgerSpecifier const& specifier, @@ -231,21 +232,21 @@ ledgerFromSpecifier( } } - return Status::kOK; + return ::rpc::Status::kOK; } template -Status +::rpc::Status getLedger(T& ledger, UInt256 const& ledgerHash, Context const& context) { ledger = context.ledgerMaster.getLedgerByHash(ledgerHash); if (ledger == nullptr) return {RpcLgrNotFound, "ledgerNotFound"}; - return Status::kOK; + return ::rpc::Status::kOK; } template -Status +::rpc::Status getLedger(T& ledger, uint32_t ledgerIndex, Context const& context) { ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex); @@ -270,11 +271,11 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context) return {RpcNotSynced, "notSynced"}; } - return Status::kOK; + return ::rpc::Status::kOK; } template -Status +::rpc::Status getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context) { if (isValidatedOld(context.ledgerMaster, context.app.config().standalone())) @@ -330,11 +331,11 @@ getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& contex return {RpcNotSynced, "notSynced"}; } } - return Status::kOK; + return ::rpc::Status::kOK; } template -Status +::rpc::Status getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context) { return std::visit( @@ -348,20 +349,20 @@ getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context cons } // Explicit instantiation of above four functions -template Status +template ::rpc::Status getLedger<>(std::shared_ptr&, uint32_t, Context const&); -template Status +template ::rpc::Status getLedger<>(std::shared_ptr&, ::rpc::spec::LedgerShortcut shortcut, Context const&); -template Status +template ::rpc::Status getLedger<>(std::shared_ptr&, UInt256 const&, Context const&); -template Status +template ::rpc::Status getLedger<>(std::shared_ptr&, ::rpc::spec::LedgerSpecifier const&, Context const&); // explicit instantiation of ledgerFromSpecifier -template Status +template ::rpc::Status ledgerFromSpecifier<>( std::shared_ptr&, org::xrpl::rpc::v1::LedgerSpecifier const&, @@ -386,18 +387,12 @@ ledgerFromSpecifier<>( // return value. Otherwise, the object contains the field "validated" and // optionally the fields "ledger_hash", "ledger_index" and // "ledger_current_index", if they are defined. -Status -lookupLedger( - std::shared_ptr& ledger, - JsonContext const& context, - json::Value& result) +void +injectLedgerFields(ReadView const& ledger, Context const& context, json::Value& result) { - if (auto status = ledgerFromRequest(ledger, context)) - return status; + auto const& info = ledger.header(); - auto& info = ledger->header(); - - if (!ledger->open()) + if (!ledger.open()) { result[jss::ledger_hash] = to_string(info.hash); result[jss::ledger_index] = info.seq; @@ -407,8 +402,20 @@ lookupLedger( result[jss::ledger_current_index] = info.seq; } - result[jss::validated] = context.ledgerMaster.isValidated(*ledger); - return Status::kOK; + result[jss::validated] = context.ledgerMaster.isValidated(ledger); +} + +::rpc::Status +lookupLedger( + std::shared_ptr& ledger, + JsonContext const& context, + json::Value& result) +{ + if (auto status = ledgerFromRequest(ledger, context)) + return status; + + injectLedgerFields(*ledger, context, result); + return ::rpc::Status::kOK; } json::Value @@ -416,7 +423,7 @@ lookupLedger(std::shared_ptr& ledger, JsonContext const& context { json::Value result; if (auto status = lookupLedger(ledger, context, result)) - status.inject(result); + injectSpecError(result, status); return result; } diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.h b/src/xrpld/rpc/detail/RPCLedgerHelpers.h index 15a35dee91..f06172e551 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.h +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -10,6 +9,7 @@ #include #include +#include #include #include @@ -39,7 +39,7 @@ struct JsonContext; * @return Status indicating success or failure of the operation. */ template -Status +::rpc::Status getLedger(T& ledger, UInt256 const& ledgerHash, Context const& context); /** @@ -57,7 +57,7 @@ getLedger(T& ledger, UInt256 const& ledgerHash, Context const& context); * @return Status indicating success or failure of the operation. */ template -Status +::rpc::Status getLedger(T& ledger, uint32_t ledgerIndex, Context const& context); /** @@ -75,7 +75,7 @@ getLedger(T& ledger, uint32_t ledgerIndex, Context const& context); * @return Status indicating success or failure of the operation. */ template -Status +::rpc::Status getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& context); /** @@ -90,9 +90,22 @@ getLedger(T& ledger, ::rpc::spec::LedgerShortcut shortcut, Context const& contex * @return Status indicating success or failure of the operation. */ template -Status +::rpc::Status getLedger(T& ledger, ::rpc::spec::LedgerSpecifier const& specifier, Context const& context); +/** + * @brief Writes the fields that identify a ledger into a result. + * + * A closed ledger gets `ledger_hash` and `ledger_index`, an open one + * `ledger_current_index`, and both get `validated`. + * + * @param ledger The ledger to describe. + * @param context The RPC context. + * @param result Reference to a json::Value to be filled with ledger data. + */ +void +injectLedgerFields(ReadView const& ledger, Context const& context, json::Value& result); + /** * @brief Looks up a ledger from a request and returns a json::Value with either * an error or ledger data. @@ -126,7 +139,7 @@ lookupLedger(std::shared_ptr&, JsonContext const&); * @param result Reference to a json::Value to be filled with ledger data. * @return Status indicating success or failure of the operation. */ -Status +::rpc::Status lookupLedger(std::shared_ptr&, JsonContext const&, json::Value& result); /** @@ -144,7 +157,7 @@ lookupLedger(std::shared_ptr&, JsonContext const&, json::Value& * @return Status indicating success or failure of the operation. */ template -Status +::rpc::Status ledgerFromRequest(T& ledger, GRPCContext const& context); /** @@ -160,7 +173,7 @@ ledgerFromRequest(T& ledger, GRPCContext const& context); * @return Status indicating success or failure of the operation. */ template -Status +::rpc::Status ledgerFromSpecifier( T& ledger, org::xrpl::rpc::v1::LedgerSpecifier const& specifier, diff --git a/src/xrpld/rpc/detail/SpecBridge.hpp b/src/xrpld/rpc/detail/SpecBridge.hpp index d99d3f777b..c7c89a4591 100644 --- a/src/xrpld/rpc/detail/SpecBridge.hpp +++ b/src/xrpld/rpc/detail/SpecBridge.hpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace xrpl::rpc { @@ -19,6 +20,10 @@ namespace xrpl::rpc { template using HandlerFor = ::rpc::spec::HandlerFor; +static_assert( + std::variant_size_v<::rpc::CombinedError> == 1, + "xrpl::rpc : an xrpld ::rpc::Status can hold only an ErrorCodeI"); + inline void injectSpecError(json::Value& object, ::rpc::Status const& status) { diff --git a/src/xrpld/rpc/detail/Status.cpp b/src/xrpld/rpc/detail/Status.cpp deleted file mode 100644 index 147f2b31e0..0000000000 --- a/src/xrpld/rpc/detail/Status.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace xrpl::rpc { - -std::string -Status::codeString() const -{ - if (!*this) - return ""; - - if (type_ == Type::None) - return std::to_string(code_); - - if (type_ == Status::Type::TER) - { - std::string s1, s2; - - [[maybe_unused]] auto const success = transResultInfo(toTER(), s1, s2); - XRPL_ASSERT(success, "xrpl::rpc::codeString : valid TER result"); - - return s1 + ": " + s2; - } - - if (type_ == Status::Type::ErrorCodeI) - { - auto info = getErrorInfo(toErrorCode()); - std::ostringstream sStr; - sStr << info.token.cStr() << ": " << info.message.cStr(); - return sStr.str(); - } - - // LCOV_EXCL_START - UNREACHABLE("xrpl::rpc::codeString : invalid type"); - return ""; - // LCOV_EXCL_STOP -} - -void -Status::fillJson(json::Value& value) -{ - if (!*this) - return; - - auto& error = value[jss::error]; - error[jss::code] = code_; - error[jss::message] = codeString(); - - // Are there any more messages? - if (!messages_.empty()) - { - auto& messages = error[jss::data]; - for (auto& i : messages_) - messages.append(i); - } -} - -std::string -Status::message() const -{ - std::string result; - for (auto& m : messages_) - { - if (!result.empty()) - result += '/'; - result += m; - } - - return result; -} - -std::string -Status::toString() const -{ - if (*this) - return codeString() + ":" + message(); - return ""; -} - -} // namespace xrpl::rpc diff --git a/src/xrpld/rpc/handlers/Handlers.h b/src/xrpld/rpc/handlers/Handlers.h index d192e8726c..0c2b2ace63 100644 --- a/src/xrpld/rpc/handlers/Handlers.h +++ b/src/xrpld/rpc/handlers/Handlers.h @@ -27,8 +27,6 @@ doAMMInfo(rpc::JsonContext&); json::Value doBookOffers(rpc::JsonContext&); json::Value -doBookChanges(rpc::JsonContext&); -json::Value doBlackList(rpc::JsonContext&); json::Value doCanDelete(rpc::JsonContext&); @@ -127,8 +125,6 @@ doSubmitMultiSigned(rpc::JsonContext&); json::Value doSubscribe(rpc::JsonContext&); json::Value -doTransactionEntry(rpc::JsonContext&); -json::Value doTxJson(rpc::JsonContext&); json::Value doTxHistory(rpc::JsonContext&); diff --git a/src/xrpld/rpc/handlers/account/AccountObjects.cpp b/src/xrpld/rpc/handlers/account/AccountObjects.cpp index 477e721196..6c4fac4458 100644 --- a/src/xrpld/rpc/handlers/account/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/account/AccountObjects.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -340,7 +341,7 @@ doAccountObjects(rpc::JsonContext& context) if (rpcStatus) { result.clear(); - rpcStatus.inject(result); + rpc::injectSpecError(result, rpcStatus); return result; } if (type != ltANY) diff --git a/src/xrpld/rpc/handlers/account/AccountTx.cpp b/src/xrpld/rpc/handlers/account/AccountTx.cpp index a688223170..6b68c9548c 100644 --- a/src/xrpld/rpc/handlers/account/AccountTx.cpp +++ b/src/xrpld/rpc/handlers/account/AccountTx.cpp @@ -5,9 +5,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -114,8 +115,8 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params) if ((params.isMember(jss::ledger_index_min) || params.isMember(jss::ledger_index_max)) && (params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index))) { - rpc::Status const status{RpcInvalidParams, "invalidParams"}; - status.inject(response); + ::rpc::Status const status{RpcInvalidParams, "invalidParams"}; + rpc::injectSpecError(response, status); return response; } } @@ -137,16 +138,16 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params) auto& hashValue = params[jss::ledger_hash]; if (!hashValue.isString()) { - rpc::Status const status{RpcInvalidParams, "ledgerHashNotString"}; - status.inject(response); + ::rpc::Status const status{RpcInvalidParams, "ledgerHashNotString"}; + rpc::injectSpecError(response, status); return response; } LedgerHash hash; if (!hash.parseHex(hashValue.asString())) { - rpc::Status const status{RpcInvalidParams, "ledgerHashMalformed"}; - status.inject(response); + ::rpc::Status const status{RpcInvalidParams, "ledgerHashMalformed"}; + rpc::injectSpecError(response, status); return response; } return hash; @@ -176,8 +177,8 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params) } else { - rpc::Status const status{RpcInvalidParams, "ledger_index string malformed"}; - status.inject(response); + ::rpc::Status const status{RpcInvalidParams, "ledger_index string malformed"}; + rpc::injectSpecError(response, status); return response; } } @@ -186,7 +187,7 @@ parseLedgerArgs(rpc::Context& context, json::Value const& params) return std::optional{}; } -std::variant +std::variant getLedgerRange(rpc::Context& context, std::optional const& ledgerSpecifier) { std::uint32_t uValidatedMin = 0; @@ -207,7 +208,7 @@ getLedgerRange(rpc::Context& context, std::optional const& ledg if (ledgerSpecifier) { auto status = std::visit( - [&](auto const& ls) -> rpc::Status { + [&](auto const& ls) -> ::rpc::Status { using T = std::decay_t; if constexpr (std::is_same_v) { @@ -255,7 +256,7 @@ getLedgerRange(rpc::Context& context, std::optional const& ledg } uLedgerMin = uLedgerMax = ledgerView->header().seq; } - return rpc::Status::kOK; + return ::rpc::Status::kOK; }, *ledgerSpecifier); @@ -265,7 +266,7 @@ getLedgerRange(rpc::Context& context, std::optional const& ledg return LedgerRange{.min = uLedgerMin, .max = uLedgerMax}; } -std::pair +std::pair doAccountTxHelp(rpc::Context& context, AccountTxArgs const& args) { context.loadType = resource::kFeeMediumBurdenRpc; @@ -273,7 +274,7 @@ doAccountTxHelp(rpc::Context& context, AccountTxArgs const& args) AccountTxResult result; auto lgrRange = getLedgerRange(context, args.ledger); - if (auto stat = std::get_if(&lgrRange)) + if (auto stat = std::get_if<::rpc::Status>(&lgrRange)) { // An error occurred getting the requested ledger range return {result, *stat}; @@ -332,15 +333,15 @@ doAccountTxHelp(rpc::Context& context, AccountTxArgs const& args) json::Value populateJsonResponse( - std::pair const& res, + std::pair const& res, AccountTxArgs const& args, rpc::JsonContext const& context) { json::Value response; - rpc::Status const& error = res.second; - if (error.toErrorCode() != RpcSuccess) + ::rpc::Status const& error = res.second; + if (error != RpcSuccess) { - error.inject(response); + rpc::injectSpecError(response, error); } else { @@ -512,11 +513,11 @@ doAccountTx(rpc::JsonContext& context) !token[jss::ledger].isConvertibleTo(json::ValueType::UInt) || !token[jss::seq].isConvertibleTo(json::ValueType::UInt)) { - rpc::Status const status{ + ::rpc::Status const status{ RpcInvalidParams, "invalid marker. Provide ledger index via ledger field, and " "transaction sequence number via seq field"}; - status.inject(response); + rpc::injectSpecError(response, status); return response; } args.marker = { @@ -546,11 +547,11 @@ doAccountTx(rpc::JsonContext& context) params[jss::marker][jss::delegate].asBool(); if (markerFromDelegate != args.delegate.has_value()) { - rpc::Status const status{ + ::rpc::Status const status{ RpcInvalidParams, "Do not mix delegate and non-delegate pagination markers in account_tx; " "repeat the same `delegate` object when using a delegate marker."}; - status.inject(response); + rpc::injectSpecError(response, status); return response; } } diff --git a/src/xrpld/rpc/handlers/ledger/Ledger.cpp b/src/xrpld/rpc/handlers/ledger/Ledger.cpp index e6d207ddf4..e5ec6fbbcb 100644 --- a/src/xrpld/rpc/handlers/ledger/Ledger.cpp +++ b/src/xrpld/rpc/handlers/ledger/Ledger.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -57,7 +56,7 @@ LedgerHandler::process(Input const& input) return output; if (auto const status = getLedger(output.ledger, input.ledger, context_.get())) - return std::unexpected{::rpc::Status{status.toErrorCode(), status.message()}}; + return std::unexpected{status}; if (input.full || input.accounts) { @@ -134,13 +133,13 @@ doLedgerGrpc(rpc::GRPCContext& context) if (auto status = rpc::ledgerFromRequest(ledger, context)) { grpc::Status errorStatus; - if (status.toErrorCode() == RpcInvalidParams) + if (status == RpcInvalidParams) { - errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message()); + errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message); } else { - errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message()); + errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message); } return {response, errorStatus}; } diff --git a/src/xrpld/rpc/handlers/ledger/Ledger.h b/src/xrpld/rpc/handlers/ledger/Ledger.h index f79f9aed82..e8e157f5df 100644 --- a/src/xrpld/rpc/handlers/ledger/Ledger.h +++ b/src/xrpld/rpc/handlers/ledger/Ledger.h @@ -3,14 +3,10 @@ #include #include // IWYU pragma: keep #include -#include -#include -#include #include #include #include -#include #include #include @@ -18,7 +14,6 @@ #include #include #include -#include #include namespace xrpl::rpc { @@ -49,18 +44,6 @@ public: void writeResult(json::Value& value, Output const& output) const; - // NOLINTBEGIN(readability-identifier-naming) - static constexpr std::string_view name = method::kLedger; - - static constexpr unsigned minApiVer = rpc::kApiMinimumSupportedVersion; - - static constexpr unsigned maxApiVer = rpc::kApiMaximumValidVersion; - - static constexpr Role role = Role::USER; - - static constexpr Condition condition = Condition::NoCondition; - // NOLINTEND(readability-identifier-naming) - private: std::reference_wrapper context_; }; diff --git a/src/xrpld/rpc/handlers/ledger/LedgerData.cpp b/src/xrpld/rpc/handlers/ledger/LedgerData.cpp index cf652feab2..d001ce5064 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerData.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerData.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -90,7 +91,7 @@ doLedgerData(rpc::JsonContext& context) if (rpcStatus) { jvResult.clear(); - rpcStatus.inject(jvResult); + rpc::injectSpecError(jvResult, rpcStatus); return jvResult; } json::Value& nodes = jvResult[jss::state]; @@ -141,13 +142,13 @@ doLedgerDataGrpc(rpc::GRPCContext& con if (auto status = rpc::ledgerFromRequest(ledger, context)) { grpc::Status errorStatus; - if (status.toErrorCode() == RpcInvalidParams) + if (status == RpcInvalidParams) { - errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message()); + errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message); } else { - errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message()); + errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message); } return {response, errorStatus}; } diff --git a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp index 2ff1719266..7d72b62fd5 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp @@ -1111,13 +1111,13 @@ doLedgerEntryGrpc(rpc::GRPCContext& c if (auto status = rpc::ledgerFromRequest(ledger, context)) { grpc::Status errorStatus; - if (status.toErrorCode() == RpcInvalidParams) + if (status == RpcInvalidParams) { - errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message()); + errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message); } else { - errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message()); + errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message); } return {response, errorStatus}; } diff --git a/src/xrpld/rpc/handlers/orderbook/BookChanges.cpp b/src/xrpld/rpc/handlers/orderbook/BookChanges.cpp index 0f796df3a4..a61e49bd0a 100644 --- a/src/xrpld/rpc/handlers/orderbook/BookChanges.cpp +++ b/src/xrpld/rpc/handlers/orderbook/BookChanges.cpp @@ -1,25 +1,36 @@ -#include +#include +#include #include #include -#include +#include #include // IWYU pragma: keep -#include +#include -namespace xrpl { +#include -json::Value -doBookChanges(rpc::JsonContext& context) +namespace xrpl::rpc { + +BookChangesHandler::BookChangesHandler(JsonContext& context) : context_(context) { - std::shared_ptr ledger; - - json::Value result = rpc::lookupLedger(ledger, context); - if (ledger == nullptr) - return result; - - return rpc::computeBookChanges(ledger); } -} // namespace xrpl +std::expected +BookChangesHandler::process(Input const& input) const +{ + Output output; + if (auto const status = getLedger(output.ledger, input.ledger, context_.get())) + return std::unexpected{status}; + + return output; +} + +void +BookChangesHandler::writeResult(json::Value& value, Output const& output) +{ + value = computeBookChanges(output.ledger); +} + +} // namespace xrpl::rpc diff --git a/src/xrpld/rpc/handlers/orderbook/BookChanges.h b/src/xrpld/rpc/handlers/orderbook/BookChanges.h new file mode 100644 index 0000000000..3f02fa745c --- /dev/null +++ b/src/xrpld/rpc/handlers/orderbook/BookChanges.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +namespace xrpl::rpc { + +class BookChangesHandler : public HandlerFor<::rpc::spec::handlers::book_changes::Input> +{ +public: + struct Output + { + std::shared_ptr ledger; + }; + + explicit BookChangesHandler(JsonContext&); + + [[nodiscard]] std::expected + process(Input const& input) const; + + static void + writeResult(json::Value& value, Output const& output); + +private: + std::reference_wrapper context_; +}; + +} // namespace xrpl::rpc diff --git a/src/xrpld/rpc/handlers/server_info/Version.h b/src/xrpld/rpc/handlers/server_info/Version.h index fe134af266..de6ed61555 100644 --- a/src/xrpld/rpc/handlers/server_info/Version.h +++ b/src/xrpld/rpc/handlers/server_info/Version.h @@ -2,9 +2,6 @@ #include // IWYU pragma: keep #include -#include -#include -#include #include #include @@ -14,7 +11,6 @@ #include #include #include -#include namespace xrpl::rpc { @@ -46,18 +42,6 @@ public: setVersion(obj, output.apiVersion, output.betaEnabled); } - // NOLINTBEGIN(readability-identifier-naming) - static constexpr std::string_view name = method::kVersion; - - static constexpr unsigned minApiVer = rpc::kApiMinimumSupportedVersion; - - static constexpr unsigned maxApiVer = rpc::kApiMaximumValidVersion; - - static constexpr Role role = Role::USER; - - static constexpr Condition condition = Condition::NoCondition; - // NOLINTEND(readability-identifier-naming) - private: std::reference_wrapper context_; }; diff --git a/src/xrpld/rpc/handlers/transaction/TransactionEntry.cpp b/src/xrpld/rpc/handlers/transaction/TransactionEntry.cpp index 2181d41857..bf1078048f 100644 --- a/src/xrpld/rpc/handlers/transaction/TransactionEntry.cpp +++ b/src/xrpld/rpc/handlers/transaction/TransactionEntry.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -9,93 +11,95 @@ #include #include -#include +#include +#include -namespace xrpl { +#include +#include +#include -// { -// ledger_hash : , -// ledger_index : -// } -// -// XXX In this case, not specify either ledger does not mean ledger current. It -// means any ledger. -json::Value -doTransactionEntry(rpc::JsonContext& context) +namespace xrpl::rpc { + +TransactionEntryHandler::TransactionEntryHandler(JsonContext& context) : context_(context) { - std::shared_ptr lpLedger; - json::Value jvResult = rpc::lookupLedger(lpLedger, context); +} - if (!lpLedger) - return jvResult; +// With no ledger specified, this uses the current ledger, as every other method +// does. An open ledger is refused with notYetImplemented, so a request with no +// ledger fails the same way as one that asks for "current". +std::expected +TransactionEntryHandler::process(Input const& input) const +{ + Output output; + if (auto const status = getLedger(output.ledger, input.ledger, context_.get())) + return std::unexpected{status}; - if (!context.params.isMember(jss::tx_hash)) + if (!input.txHash && + input.txHash.error() == ::rpc::spec::handlers::transaction_entry::TxHashError::Missing) { - jvResult[jss::error] = "fieldNotFoundTransaction"; + output.error = "fieldNotFoundTransaction"; } - else if (jvResult.get(jss::ledger_hash, json::ValueType::Null).isNull()) + else if (output.ledger->open()) { // We don't work on ledger current. - - // XXX We don't support any transaction yet. - jvResult[jss::error] = "notYetImplemented"; + output.error = "notYetImplemented"; + } + else if (!input.txHash) + { + output.error = "malformedRequest"; } else { - UInt256 uTransID; - // XXX Relying on trusted WSS client. Would be better to have a strict - // routine, returning success or failure. - if (!uTransID.parseHex(context.params[jss::tx_hash].asString())) - { - jvResult[jss::error] = "malformedRequest"; - return jvResult; - } - - auto [sttx, stobj] = lpLedger->txRead(uTransID); - if (!sttx) - { - jvResult[jss::error] = "transactionNotFound"; - } - else - { - if (context.apiVersion > 1) - { - jvResult[jss::tx_json] = sttx->getJson(JsonOptions::Values::DisableApiPriorV2); - jvResult[jss::hash] = to_string(sttx->getTransactionID()); - - if (!lpLedger->open()) - { - jvResult[jss::ledger_hash] = - to_string(context.ledgerMaster.getHashBySeq(lpLedger->seq())); - } - - bool const validated = context.ledgerMaster.isValidated(*lpLedger); - - jvResult[jss::validated] = validated; - if (validated) - { - jvResult[jss::ledger_index] = lpLedger->seq(); - if (auto closeTime = context.ledgerMaster.getCloseTimeBySeq(lpLedger->seq())) - jvResult[jss::close_time_iso] = toStringIso(*closeTime); - } - } - else - { - jvResult[jss::tx_json] = sttx->getJson(JsonOptions::Values::None); - } - - rpc::insertDeliverMax(jvResult[jss::tx_json], sttx->getTxnType(), context.apiVersion); - - auto const jsonMeta = (context.apiVersion > 1 ? jss::meta : jss::metadata); - if (stobj) - jvResult[jsonMeta] = stobj->getJson(JsonOptions::Values::None); - // 'accounts' - // 'engine_...' - // 'ledger_...' - } + std::tie(output.tx, output.meta) = output.ledger->txRead(*input.txHash); + if (!output.tx) + output.error = "transactionNotFound"; } - return jvResult; + return output; } -} // namespace xrpl +void +TransactionEntryHandler::writeResult(json::Value& value, Output const& output) const +{ + auto const& context = context_.get(); + auto const& ledger = *output.ledger; + + injectLedgerFields(ledger, context, value); + + if (output.error) + { + value[jss::error] = std::string{*output.error}; + return; + } + + if (context.apiVersion > 1) + { + value[jss::tx_json] = output.tx->getJson(JsonOptions::Values::DisableApiPriorV2); + value[jss::hash] = to_string(output.tx->getTransactionID()); + + if (!ledger.open()) + value[jss::ledger_hash] = to_string(context.ledgerMaster.getHashBySeq(ledger.seq())); + + bool const validated = context.ledgerMaster.isValidated(ledger); + + value[jss::validated] = validated; + if (validated) + { + value[jss::ledger_index] = ledger.seq(); + if (auto closeTime = context.ledgerMaster.getCloseTimeBySeq(ledger.seq())) + value[jss::close_time_iso] = toStringIso(*closeTime); + } + } + else + { + value[jss::tx_json] = output.tx->getJson(JsonOptions::Values::None); + } + + insertDeliverMax(value[jss::tx_json], output.tx->getTxnType(), context.apiVersion); + + auto const jsonMeta = (context.apiVersion > 1 ? jss::meta : jss::metadata); + if (output.meta) + value[jsonMeta] = output.meta->getJson(JsonOptions::Values::None); +} + +} // namespace xrpl::rpc diff --git a/src/xrpld/rpc/handlers/transaction/TransactionEntry.h b/src/xrpld/rpc/handlers/transaction/TransactionEntry.h new file mode 100644 index 0000000000..68785dc188 --- /dev/null +++ b/src/xrpld/rpc/handlers/transaction/TransactionEntry.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace xrpl::rpc { + +class TransactionEntryHandler : public HandlerFor<::rpc::spec::handlers::transaction_entry::Input> +{ +public: + struct Output + { + std::shared_ptr ledger; + std::optional error; + std::shared_ptr tx; + std::shared_ptr meta; + }; + + explicit TransactionEntryHandler(JsonContext&); + + [[nodiscard]] std::expected + process(Input const& input) const; + + void + writeResult(json::Value& value, Output const& output) const; + +private: + std::reference_wrapper context_; +}; + +} // namespace xrpl::rpc diff --git a/src/xrpld/rpc/handlers/transaction/Tx.cpp b/src/xrpld/rpc/handlers/transaction/Tx.cpp index 7230ba5db7..dbda1a3ad7 100644 --- a/src/xrpld/rpc/handlers/transaction/Tx.cpp +++ b/src/xrpld/rpc/handlers/transaction/Tx.cpp @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #include #include @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -68,7 +70,7 @@ struct TxArgs std::optional> ledgerRange; }; -std::pair +std::pair doTxHelp(rpc::Context& context, TxArgs args) { TxResult result; @@ -178,25 +180,25 @@ doTxHelp(rpc::Context& context, TxArgs args) json::Value populateJsonResponse( - std::pair const& res, + std::pair const& res, TxArgs const& args, rpc::JsonContext const& context) { json::Value response; - rpc::Status const& error = res.second; + ::rpc::Status const& error = res.second; TxResult const& result = res.first; // handle errors - if (error.toErrorCode() != RpcSuccess) + if (error != RpcSuccess) { - if (error.toErrorCode() == RpcTxnNotFound && result.searchedAll != TxSearched::Unknown) + if (error == RpcTxnNotFound && result.searchedAll != TxSearched::Unknown) { response = json::Value(json::ValueType::Object); response[jss::searched_all] = (result.searchedAll == TxSearched::All); - error.inject(response); + rpc::injectSpecError(response, error); } else { - error.inject(response); + rpc::injectSpecError(response, error); } } // no errors @@ -325,7 +327,7 @@ doTxJson(rpc::JsonContext& context) } } - std::pair const res = doTxHelp(context, args); + std::pair const res = doTxHelp(context, args); return populateJsonResponse(res, args, context); }