Skip to content

Commit

Permalink
Account tx v1 api support (XRPLF#874)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
kuznetsss authored Sep 28, 2023
1 parent 963685d commit 6ca777e
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 144 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,16 +224,16 @@ a database in each region, and the Clio nodes in each region use their region's
This is effectively two systems.

Clio supports API versioning as [described here](https://xrpl.org/request-formatting.html#api-versioning).
It's possible to configure `minimum`, `maximum` and `default` version like so:
It's possible to configure `minimum`, `maximum` and `default` version like so:
```json
"api_version": {
"min": 1,
"max": 2,
"default": 2
"default": 1
}
```
All of the above are optional.
Clio will fallback to hardcoded defaults when not specified in the config file or configured values are outside
All of the above are optional.
Clio will fallback to hardcoded defaults when not specified in the config file or configured values are outside
of the minimum and maximum supported versions hardcoded in `src/rpc/common/APIVersion.h`.
> **Note:** See `example-config.json` for more details.
Expand Down
6 changes: 3 additions & 3 deletions example-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@
// "ssl_cert_file" : "/full/path/to/cert.file",
// "ssl_key_file" : "/full/path/to/key.file"
"api_version": {
"min": 2,
"max": 2,
"default": 2 // Clio only supports API v2 and newer
"min": 1, // Minimum API version supported (could be 1 or 2)
"max": 2, // Maximum API version supported (could be 1 or 2, but >= min)
"default": 1 // Clio behaves the same as rippled by default
}
}
46 changes: 46 additions & 0 deletions src/rpc/common/JsonBool.h
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions src/rpc/common/Specs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
28 changes: 20 additions & 8 deletions src/rpc/handlers/AccountTx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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();
Expand Down
18 changes: 12 additions & 6 deletions src/rpc/handlers/AccountTx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -76,8 +77,8 @@ class AccountTxHandler
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;
Expand All @@ -92,14 +93,12 @@ class AccountTxHandler
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),
Expand All @@ -121,7 +120,14 @@ class AccountTxHandler
},
};

return rpcSpec;
static auto const rpcSpec = RpcSpec{
rpcSpecForV1,
{
{JS(binary), validation::Type<bool>{}},
{JS(forward), validation::Type<bool>{}},
}};

return apiVersion == 1 ? rpcSpecForV1 : rpcSpec;
}

Result
Expand Down
Loading

0 comments on commit 6ca777e

Please sign in to comment.