mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-09 21:28:03 +00:00
refactor: Migrate handlers to rpc-spec (B) (#8484)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
#include <xrpld/rpc/Status.h>
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
|
||||
class codeString_test : public beast::unit_test::Suite
|
||||
{
|
||||
private:
|
||||
template <typename Type>
|
||||
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 <typename Type>
|
||||
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 <typename Type>
|
||||
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>(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
|
||||
@@ -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"};
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
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
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
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<std::string>;
|
||||
|
||||
static constexpr Code kOK = 0;
|
||||
|
||||
Status() = default;
|
||||
|
||||
// The constraint allows only integers (not enums). Prevents enum narrowing.
|
||||
template <typename T>
|
||||
Status(T code, Strings d = {})
|
||||
requires(std::is_integral_v<T>)
|
||||
: 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
|
||||
@@ -3,17 +3,17 @@
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/MethodNames.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
#include <xrpld/rpc/handlers/Handlers.h>
|
||||
#include <xrpld/rpc/handlers/ledger/Ledger.h>
|
||||
#include <xrpld/rpc/handlers/orderbook/BookChanges.h>
|
||||
#include <xrpld/rpc/handlers/server_info/Version.h>
|
||||
#include <xrpld/rpc/handlers/transaction/TransactionEntry.h>
|
||||
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/Types.hpp>
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <expected>
|
||||
#include <iterator>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -41,7 +40,7 @@ using Method = Handler::Method;
|
||||
* names a plain function instead of returning a closure over it.
|
||||
*/
|
||||
template <json::Value (*Function)(JsonContext&)>
|
||||
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 <typename HandlerImpl>
|
||||
@@ -63,7 +62,7 @@ template <typename HandlerImpl>
|
||||
concept InputlessHandler = requires(HandlerImpl& handler) { handler.process(); };
|
||||
|
||||
template <class HandlerImpl, class Output>
|
||||
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<ErrorCodeI>(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 <class HandlerImpl>
|
||||
requires SpecBasedHandler<HandlerImpl> or InputlessHandler<HandlerImpl>
|
||||
Status
|
||||
::rpc::Status
|
||||
handle(JsonContext& context, json::Value& object)
|
||||
requires SpecBasedHandler<HandlerImpl> or InputlessHandler<HandlerImpl>
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
context.apiVersion >= HandlerImpl::minApiVer &&
|
||||
context.apiVersion <= HandlerImpl::maxApiVer,
|
||||
"xrpl::rpc::handle : valid API version");
|
||||
|
||||
if constexpr (SpecBasedHandler<HandlerImpl>)
|
||||
{
|
||||
auto const input = HandlerImpl::parseInput(context.params, context.apiVersion);
|
||||
if (not input.has_value())
|
||||
{
|
||||
injectSpecError(object, input.error());
|
||||
return std::get<ErrorCodeI>(input.error().code);
|
||||
return input.error();
|
||||
}
|
||||
|
||||
HandlerImpl handler(context);
|
||||
@@ -115,28 +111,7 @@ handle(JsonContext& context, json::Value& object)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename HandlerImpl>
|
||||
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>>(),
|
||||
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<Handler>({
|
||||
constexpr auto kHandlerArray = std::to_array<Handler>({
|
||||
// Request-response methods
|
||||
{
|
||||
.name = method::kAccountInfo,
|
||||
@@ -201,7 +176,7 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
|
||||
},
|
||||
{
|
||||
.name = method::kBookChanges,
|
||||
.valueMethod = Method::of<&byRef<&doBookChanges>>(),
|
||||
.valueMethod = Method::of<&handle<BookChangesHandler>>(),
|
||||
.role = Role::USER,
|
||||
.condition = Condition::NoCondition,
|
||||
},
|
||||
@@ -285,6 +260,12 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
|
||||
.condition = Condition::NoCondition,
|
||||
.hasCommandLineForm = false,
|
||||
},
|
||||
{
|
||||
.name = method::kLedger,
|
||||
.valueMethod = Method::of<&handle<LedgerHandler>>(),
|
||||
.role = Role::USER,
|
||||
.condition = Condition::NoCondition,
|
||||
},
|
||||
{
|
||||
.name = method::kLedgerAccept,
|
||||
.valueMethod = Method::of<&byRef<&doLedgerAccept>>(),
|
||||
@@ -492,7 +473,7 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
|
||||
},
|
||||
{
|
||||
.name = method::kTransactionEntry,
|
||||
.valueMethod = Method::of<&byRef<&doTransactionEntry>>(),
|
||||
.valueMethod = Method::of<&handle<TransactionEntryHandler>>(),
|
||||
.role = Role::USER,
|
||||
.condition = Condition::NoCondition,
|
||||
},
|
||||
@@ -555,6 +536,12 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
|
||||
.role = Role::USER,
|
||||
.condition = Condition::NoCondition,
|
||||
},
|
||||
{
|
||||
.name = method::kVersion,
|
||||
.valueMethod = Method::of<&handle<VersionHandler>>(),
|
||||
.role = Role::USER,
|
||||
.condition = Condition::NoCondition,
|
||||
},
|
||||
{
|
||||
.name = method::kWalletPropose,
|
||||
.valueMethod = Method::of<&byRef<&doWalletPropose>>(),
|
||||
@@ -576,39 +563,9 @@ constexpr auto kFunctionHandlerArray = std::to_array<Handler>({
|
||||
},
|
||||
});
|
||||
|
||||
// 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<Handler>({
|
||||
handlerFrom<LedgerHandler>(),
|
||||
handlerFrom<VersionHandler>(),
|
||||
});
|
||||
|
||||
/**
|
||||
* 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<std::size(kFunctionHandlerArray)>{};
|
||||
constexpr auto kClassIndices = std::make_index_sequence<std::size(kClassHandlerArray)>{};
|
||||
|
||||
return []<std::size_t... Function, std::size_t... Class>(
|
||||
std::index_sequence<Function...>, std::index_sequence<Class...>) {
|
||||
return std::array<Handler, sizeof...(Function) + sizeof...(Class)>{
|
||||
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);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpld/rpc/RPCHandler.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
@@ -13,6 +12,8 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Handler.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
@@ -18,6 +17,8 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
@@ -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<std::uint64_t> 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;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/DeliveredAmount.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
@@ -12,7 +11,6 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -33,6 +31,8 @@
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -370,10 +370,10 @@ keypairForSignature(json::Value const& params, json::Value& error, unsigned int
|
||||
return generateKeyPair(*keyType, *seed);
|
||||
}
|
||||
|
||||
std::pair<rpc::Status, LedgerEntryType>
|
||||
std::pair<::rpc::Status, LedgerEntryType>
|
||||
chooseLedgerEntryType(json::Value const& params)
|
||||
{
|
||||
std::pair<rpc::Status, LedgerEntryType> 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);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
@@ -21,6 +20,8 @@
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
@@ -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<rpc::Status, LedgerEntryType>
|
||||
std::pair<::rpc::Status, LedgerEntryType>
|
||||
chooseLedgerEntryType(json::Value const& params);
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <xrpld/app/ledger/LedgerToJson.h>
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <org/xrpl/rpc/v1/get_ledger_data.pb.h> // IWYU pragma: keep
|
||||
#include <org/xrpl/rpc/v1/get_ledger_entry.pb.h> // IWYU pragma: keep
|
||||
#include <org/xrpl/rpc/v1/ledger.pb.h>
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/Ledger.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -56,7 +57,7 @@ isValidatedOld(LedgerMaster& ledgerMaster, bool standalone)
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Status
|
||||
::rpc::Status
|
||||
ledgerFromHash(
|
||||
T& ledger,
|
||||
json::Value hash,
|
||||
@@ -70,7 +71,7 @@ ledgerFromHash(
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Status
|
||||
::rpc::Status
|
||||
ledgerFromIndex(
|
||||
T& ledger,
|
||||
json::Value indexValue,
|
||||
@@ -96,7 +97,7 @@ ledgerFromIndex(
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Status
|
||||
::rpc::Status
|
||||
ledgerFromRequest(T& ledger, JsonContext const& context)
|
||||
{
|
||||
ledger.reset();
|
||||
@@ -163,7 +164,7 @@ ledgerFromRequest(T& ledger, JsonContext const& context)
|
||||
} // namespace
|
||||
|
||||
template <class T, class R>
|
||||
Status
|
||||
::rpc::Status
|
||||
ledgerFromRequest(T& ledger, GRPCContext<R> const& context)
|
||||
{
|
||||
R const& request = context.params;
|
||||
@@ -171,25 +172,25 @@ ledgerFromRequest(T& ledger, GRPCContext<R> const& context)
|
||||
}
|
||||
|
||||
// explicit instantiation of above function
|
||||
template Status
|
||||
template ::rpc::Status
|
||||
ledgerFromRequest<>(
|
||||
std::shared_ptr<ReadView const>&,
|
||||
GRPCContext<org::xrpl::rpc::v1::GetLedgerEntryRequest> const&);
|
||||
|
||||
// explicit instantiation of above function
|
||||
template Status
|
||||
template ::rpc::Status
|
||||
ledgerFromRequest<>(
|
||||
std::shared_ptr<ReadView const>&,
|
||||
GRPCContext<org::xrpl::rpc::v1::GetLedgerDataRequest> const&);
|
||||
|
||||
// explicit instantiation of above function
|
||||
template Status
|
||||
template ::rpc::Status
|
||||
ledgerFromRequest<>(
|
||||
std::shared_ptr<ReadView const>&,
|
||||
GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest> const&);
|
||||
|
||||
template <class T>
|
||||
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 <class T>
|
||||
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 <class T>
|
||||
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 <class T>
|
||||
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 <class T>
|
||||
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<ReadView const>&, uint32_t, Context const&);
|
||||
|
||||
template Status
|
||||
template ::rpc::Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerShortcut shortcut, Context const&);
|
||||
|
||||
template Status
|
||||
template ::rpc::Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, UInt256 const&, Context const&);
|
||||
|
||||
template Status
|
||||
template ::rpc::Status
|
||||
getLedger<>(std::shared_ptr<ReadView const>&, ::rpc::spec::LedgerSpecifier const&, Context const&);
|
||||
|
||||
// explicit instantiation of ledgerFromSpecifier
|
||||
template Status
|
||||
template ::rpc::Status
|
||||
ledgerFromSpecifier<>(
|
||||
std::shared_ptr<ReadView const>&,
|
||||
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<ReadView const>& 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<ReadView const>& 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<ReadView const>& ledger, JsonContext const& context
|
||||
{
|
||||
json::Value result;
|
||||
if (auto status = lookupLedger(ledger, context, result))
|
||||
status.inject(result);
|
||||
injectSpecError(result, status);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -10,6 +9,7 @@
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
#include <org/xrpl/rpc/v1/ledger.pb.h>
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/Ledger.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -39,7 +39,7 @@ struct JsonContext;
|
||||
* @return Status indicating success or failure of the operation.
|
||||
*/
|
||||
template <class T>
|
||||
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 <class T>
|
||||
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 <class T>
|
||||
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 <class T>
|
||||
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<ReadView const>&, 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<ReadView const>&, JsonContext const&, json::Value& result);
|
||||
|
||||
/**
|
||||
@@ -144,7 +157,7 @@ lookupLedger(std::shared_ptr<ReadView const>&, JsonContext const&, json::Value&
|
||||
* @return Status indicating success or failure of the operation.
|
||||
*/
|
||||
template <class T, class R>
|
||||
Status
|
||||
::rpc::Status
|
||||
ledgerFromRequest(T& ledger, GRPCContext<R> const& context);
|
||||
|
||||
/**
|
||||
@@ -160,7 +173,7 @@ ledgerFromRequest(T& ledger, GRPCContext<R> const& context);
|
||||
* @return Status indicating success or failure of the operation.
|
||||
*/
|
||||
template <class T>
|
||||
Status
|
||||
::rpc::Status
|
||||
ledgerFromSpecifier(
|
||||
T& ledger,
|
||||
org::xrpl::rpc::v1::LedgerSpecifier const& specifier,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
@@ -19,6 +20,10 @@ namespace xrpl::rpc {
|
||||
template <typename InputT>
|
||||
using HandlerFor = ::rpc::spec::HandlerFor<InputT, json::Value>;
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
#include <xrpld/rpc/Status.h>
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
@@ -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&);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -340,7 +341,7 @@ doAccountObjects(rpc::JsonContext& context)
|
||||
if (rpcStatus)
|
||||
{
|
||||
result.clear();
|
||||
rpcStatus.inject(result);
|
||||
rpc::injectSpecError(result, rpcStatus);
|
||||
return result;
|
||||
}
|
||||
if (type != ltANY)
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
#include <xrpld/rpc/detail/SyntheticFields.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <xrpl/rdb/RelationalDatabase.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/Ledger.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -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<LedgerSpecifier>{};
|
||||
}
|
||||
|
||||
std::variant<LedgerRange, rpc::Status>
|
||||
std::variant<LedgerRange, ::rpc::Status>
|
||||
getLedgerRange(rpc::Context& context, std::optional<LedgerSpecifier> const& ledgerSpecifier)
|
||||
{
|
||||
std::uint32_t uValidatedMin = 0;
|
||||
@@ -207,7 +208,7 @@ getLedgerRange(rpc::Context& context, std::optional<LedgerSpecifier> const& ledg
|
||||
if (ledgerSpecifier)
|
||||
{
|
||||
auto status = std::visit(
|
||||
[&](auto const& ls) -> rpc::Status {
|
||||
[&](auto const& ls) -> ::rpc::Status {
|
||||
using T = std::decay_t<decltype(ls)>;
|
||||
if constexpr (std::is_same_v<T, LedgerRange>)
|
||||
{
|
||||
@@ -255,7 +256,7 @@ getLedgerRange(rpc::Context& context, std::optional<LedgerSpecifier> 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<LedgerSpecifier> const& ledg
|
||||
return LedgerRange{.min = uLedgerMin, .max = uLedgerMax};
|
||||
}
|
||||
|
||||
std::pair<AccountTxResult, rpc::Status>
|
||||
std::pair<AccountTxResult, ::rpc::Status>
|
||||
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<rpc::Status>(&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<AccountTxResult, rpc::Status> const& res,
|
||||
std::pair<AccountTxResult, ::rpc::Status> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/GRPCHandlers.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
@@ -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<org::xrpl::rpc::v1::GetLedgerRequest>& 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};
|
||||
}
|
||||
|
||||
@@ -3,14 +3,10 @@
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/app/misc/TxQ.h> // IWYU pragma: keep
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/MethodNames.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/detail/Handler.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/handlers/ledger/Types.hpp>
|
||||
@@ -18,7 +14,6 @@
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
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<JsonContext> context_;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -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<org::xrpl::rpc::v1::GetLedgerDataRequest>& 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};
|
||||
}
|
||||
|
||||
@@ -1111,13 +1111,13 @@ doLedgerEntryGrpc(rpc::GRPCContext<org::xrpl::rpc::v1::GetLedgerEntryRequest>& 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};
|
||||
}
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
#include <xrpld/rpc/BookChanges.h>
|
||||
#include <xrpld/rpc/handlers/orderbook/BookChanges.h>
|
||||
|
||||
#include <xrpld/rpc/BookChanges.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/STArray.h> // IWYU pragma: keep
|
||||
|
||||
#include <memory>
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
#include <expected>
|
||||
|
||||
json::Value
|
||||
doBookChanges(rpc::JsonContext& context)
|
||||
namespace xrpl::rpc {
|
||||
|
||||
BookChangesHandler::BookChangesHandler(JsonContext& context) : context_(context)
|
||||
{
|
||||
std::shared_ptr<ReadView const> ledger;
|
||||
|
||||
json::Value result = rpc::lookupLedger(ledger, context);
|
||||
if (ledger == nullptr)
|
||||
return result;
|
||||
|
||||
return rpc::computeBookChanges(ledger);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
std::expected<BookChangesHandler::Output, ::rpc::Status>
|
||||
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
|
||||
|
||||
38
src/xrpld/rpc/handlers/orderbook/BookChanges.h
Normal file
38
src/xrpld/rpc/handlers/orderbook/BookChanges.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/handlers/book_changes/Types.hpp>
|
||||
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
|
||||
class BookChangesHandler : public HandlerFor<::rpc::spec::handlers::book_changes::Input>
|
||||
{
|
||||
public:
|
||||
struct Output
|
||||
{
|
||||
std::shared_ptr<ReadView const> ledger;
|
||||
};
|
||||
|
||||
explicit BookChangesHandler(JsonContext&);
|
||||
|
||||
[[nodiscard]] std::expected<Output, ::rpc::Status>
|
||||
process(Input const& input) const;
|
||||
|
||||
static void
|
||||
writeResult(json::Value& value, Output const& output);
|
||||
|
||||
private:
|
||||
std::reference_wrapper<JsonContext> context_;
|
||||
};
|
||||
|
||||
} // namespace xrpl::rpc
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
#include <xrpld/app/main/Application.h> // IWYU pragma: keep
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/MethodNames.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/detail/Handler.h>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
@@ -14,7 +11,6 @@
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
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<JsonContext> context_;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <xrpld/rpc/handlers/transaction/TransactionEntry.h>
|
||||
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/misc/DeliverMax.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
@@ -9,93 +11,95 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <memory>
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/handlers/transaction_entry/Types.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
#include <expected>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
// {
|
||||
// ledger_hash : <ledger>,
|
||||
// ledger_index : <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<ReadView const> 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::Output, ::rpc::Status>
|
||||
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
|
||||
|
||||
45
src/xrpld/rpc/handlers/transaction/TransactionEntry.h
Normal file
45
src/xrpld/rpc/handlers/transaction/TransactionEntry.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
#include <rpcspec/handlers/transaction_entry/Types.hpp>
|
||||
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
|
||||
class TransactionEntryHandler : public HandlerFor<::rpc::spec::handlers::transaction_entry::Input>
|
||||
{
|
||||
public:
|
||||
struct Output
|
||||
{
|
||||
std::shared_ptr<ReadView const> ledger;
|
||||
std::optional<std::string_view> error;
|
||||
std::shared_ptr<STTx const> tx;
|
||||
std::shared_ptr<STObject const> meta;
|
||||
};
|
||||
|
||||
explicit TransactionEntryHandler(JsonContext&);
|
||||
|
||||
[[nodiscard]] std::expected<Output, ::rpc::Status>
|
||||
process(Input const& input) const;
|
||||
|
||||
void
|
||||
writeResult(json::Value& value, Output const& output) const;
|
||||
|
||||
private:
|
||||
std::reference_wrapper<JsonContext> context_;
|
||||
};
|
||||
|
||||
} // namespace xrpl::rpc
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <xrpld/rpc/CTID.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/DeliveredAmount.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||
#include <xrpld/rpc/detail/SpecBridge.hpp>
|
||||
#include <xrpld/rpc/detail/SyntheticFields.h>
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <xrpl/rdb/RelationalDatabase.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
#include <rpcspec/Errors.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -68,7 +70,7 @@ struct TxArgs
|
||||
std::optional<std::pair<uint32_t, uint32_t>> ledgerRange;
|
||||
};
|
||||
|
||||
std::pair<TxResult, rpc::Status>
|
||||
std::pair<TxResult, ::rpc::Status>
|
||||
doTxHelp(rpc::Context& context, TxArgs args)
|
||||
{
|
||||
TxResult result;
|
||||
@@ -178,25 +180,25 @@ doTxHelp(rpc::Context& context, TxArgs args)
|
||||
|
||||
json::Value
|
||||
populateJsonResponse(
|
||||
std::pair<TxResult, rpc::Status> const& res,
|
||||
std::pair<TxResult, ::rpc::Status> 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<TxResult, rpc::Status> const res = doTxHelp(context, args);
|
||||
std::pair<TxResult, ::rpc::Status> const res = doTxHelp(context, args);
|
||||
return populateJsonResponse(res, args, context);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user