mirror of
https://github.com/XRPLF/clio.git
synced 2026-04-29 15:37:53 +00:00
Account tx v1 api support (#874)
* Don't fail on ledger params for v1 * Different error on invalid ledger indexes for v1 * Allow forward and binary to be not bool for v1 * Minor fixes * Fix tests * Don't fail if input ledger index is out of range for v1 * Restore deleted test * Fix comparison of integers with different signedness * Updated default api version in README and example config
This commit is contained in:
46
src/rpc/common/JsonBool.h
Normal file
46
src/rpc/common/JsonBool.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/json/value_to.hpp>
|
||||
namespace rpc {
|
||||
|
||||
/**
|
||||
* @brief A wrapper around bool that allows to convert from any JSON value
|
||||
*/
|
||||
struct JsonBool
|
||||
{
|
||||
bool value = false;
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
inline JsonBool
|
||||
tag_invoke(boost::json::value_to_tag<JsonBool> const&, boost::json::value const& jsonValue)
|
||||
{
|
||||
switch (jsonValue.kind())
|
||||
{
|
||||
case boost::json::kind::null:
|
||||
return JsonBool{false};
|
||||
case boost::json::kind::bool_:
|
||||
return JsonBool{jsonValue.as_bool()};
|
||||
case boost::json::kind::uint64:
|
||||
[[fallthrough]];
|
||||
case boost::json::kind::int64:
|
||||
return JsonBool{jsonValue.as_int64() != 0};
|
||||
case boost::json::kind::double_:
|
||||
return JsonBool{jsonValue.as_double() != 0.0};
|
||||
case boost::json::kind::string:
|
||||
// Also should be `jsonValue.as_string() != "false"` but rippled doesn't do that. Anyway for v2 api we have
|
||||
// bool validation
|
||||
return JsonBool{!jsonValue.as_string().empty() && jsonValue.as_string()[0] != 0};
|
||||
case boost::json::kind::array:
|
||||
return JsonBool{!jsonValue.as_array().empty()};
|
||||
case boost::json::kind::object:
|
||||
return JsonBool{!jsonValue.as_object().empty()};
|
||||
}
|
||||
throw std::runtime_error("Invalid json value");
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
@@ -76,6 +76,18 @@ struct RpcSpec final
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a full RPC request specification from another spec and additional fields.
|
||||
*
|
||||
* @param other The other spec to copy fields from
|
||||
* @param additionalFields The additional fields to add to the spec
|
||||
*/
|
||||
RpcSpec(const RpcSpec& other, std::initializer_list<FieldSpec> additionalFields) : fields_{other.fields_}
|
||||
{
|
||||
for (auto& f : additionalFields)
|
||||
fields_.push_back(std::move(f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processos the passed JSON value using the stored field specs.
|
||||
*
|
||||
|
||||
@@ -72,27 +72,39 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
|
||||
|
||||
if (input.ledgerIndexMin)
|
||||
{
|
||||
if (range->maxSequence < input.ledgerIndexMin || range->minSequence > input.ledgerIndexMin)
|
||||
if (ctx.apiVersion > 1u &&
|
||||
(input.ledgerIndexMin > range->maxSequence || input.ledgerIndexMin < range->minSequence))
|
||||
{
|
||||
return Error{Status{RippledError::rpcLGR_IDX_MALFORMED, "ledgerSeqMinOutOfRange"}};
|
||||
}
|
||||
|
||||
minIndex = *input.ledgerIndexMin;
|
||||
if (static_cast<std::uint32_t>(*input.ledgerIndexMin) > minIndex)
|
||||
minIndex = *input.ledgerIndexMin;
|
||||
}
|
||||
|
||||
if (input.ledgerIndexMax)
|
||||
{
|
||||
if (range->maxSequence < input.ledgerIndexMax || range->minSequence > input.ledgerIndexMax)
|
||||
if (ctx.apiVersion > 1u &&
|
||||
(input.ledgerIndexMax > range->maxSequence || input.ledgerIndexMax < range->minSequence))
|
||||
{
|
||||
return Error{Status{RippledError::rpcLGR_IDX_MALFORMED, "ledgerSeqMaxOutOfRange"}};
|
||||
}
|
||||
|
||||
maxIndex = *input.ledgerIndexMax;
|
||||
if (static_cast<std::uint32_t>(*input.ledgerIndexMax) < maxIndex)
|
||||
maxIndex = *input.ledgerIndexMax;
|
||||
}
|
||||
|
||||
if (minIndex > maxIndex)
|
||||
{
|
||||
if (ctx.apiVersion == 1u)
|
||||
return Error{Status{RippledError::rpcLGR_IDXS_INVALID}};
|
||||
|
||||
return Error{Status{RippledError::rpcINVALID_LGR_RANGE}};
|
||||
}
|
||||
|
||||
if (input.ledgerHash || input.ledgerIndex || input.usingValidatedLedger)
|
||||
{
|
||||
// rippled does not have this check
|
||||
if (input.ledgerIndexMax || input.ledgerIndexMin)
|
||||
if (ctx.apiVersion > 1u && (input.ledgerIndexMax || input.ledgerIndexMin))
|
||||
return Error{Status{RippledError::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"}};
|
||||
|
||||
auto const lgrInfoOrStatus = getLedgerInfoFromHashOrSeq(
|
||||
@@ -247,10 +259,10 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
|
||||
}
|
||||
|
||||
if (jsonObject.contains(JS(binary)))
|
||||
input.binary = jsonObject.at(JS(binary)).as_bool();
|
||||
input.binary = boost::json::value_to<JsonBool>(jsonObject.at(JS(binary)));
|
||||
|
||||
if (jsonObject.contains(JS(forward)))
|
||||
input.forward = jsonObject.at(JS(forward)).as_bool();
|
||||
input.forward = boost::json::value_to<JsonBool>(jsonObject.at(JS(forward)));
|
||||
|
||||
if (jsonObject.contains(JS(limit)))
|
||||
input.limit = jsonObject.at(JS(limit)).as_int64();
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <data/BackendInterface.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <rpc/common/JsonBool.h>
|
||||
#include <rpc/common/MetaProcessors.h>
|
||||
#include <rpc/common/Modifiers.h>
|
||||
#include <rpc/common/Types.h>
|
||||
@@ -76,8 +77,8 @@ public:
|
||||
std::optional<int32_t> ledgerIndexMin;
|
||||
std::optional<int32_t> ledgerIndexMax;
|
||||
bool usingValidatedLedger = false;
|
||||
bool binary = false;
|
||||
bool forward = false;
|
||||
JsonBool binary{false};
|
||||
JsonBool forward{false};
|
||||
std::optional<uint32_t> limit;
|
||||
std::optional<Marker> marker;
|
||||
std::optional<ripple::TxType> transactionType;
|
||||
@@ -92,14 +93,12 @@ public:
|
||||
RpcSpecConstRef
|
||||
spec([[maybe_unused]] uint32_t apiVersion) const
|
||||
{
|
||||
static auto const rpcSpec = RpcSpec{
|
||||
static auto const rpcSpecForV1 = RpcSpec{
|
||||
{JS(account), validation::Required{}, validation::AccountValidator},
|
||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||
{JS(ledger_index), validation::LedgerIndexValidator},
|
||||
{JS(ledger_index_min), validation::Type<int32_t>{}},
|
||||
{JS(ledger_index_max), validation::Type<int32_t>{}},
|
||||
{JS(binary), validation::Type<bool>{}},
|
||||
{JS(forward), validation::Type<bool>{}},
|
||||
{JS(limit),
|
||||
validation::Type<uint32_t>{},
|
||||
validation::Min(1u),
|
||||
@@ -121,7 +120,14 @@ public:
|
||||
},
|
||||
};
|
||||
|
||||
return rpcSpec;
|
||||
static auto const rpcSpec = RpcSpec{
|
||||
rpcSpecForV1,
|
||||
{
|
||||
{JS(binary), validation::Type<bool>{}},
|
||||
{JS(forward), validation::Type<bool>{}},
|
||||
}};
|
||||
|
||||
return apiVersion == 1 ? rpcSpecForV1 : rpcSpec;
|
||||
}
|
||||
|
||||
Result
|
||||
|
||||
Reference in New Issue
Block a user