mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-05 04:15:51 +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:
@@ -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.
|
This is effectively two systems.
|
||||||
|
|
||||||
Clio supports API versioning as [described here](https://xrpl.org/request-formatting.html#api-versioning).
|
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
|
```json
|
||||||
"api_version": {
|
"api_version": {
|
||||||
"min": 1,
|
"min": 1,
|
||||||
"max": 2,
|
"max": 2,
|
||||||
"default": 2
|
"default": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
All of the above are optional.
|
All of the above are optional.
|
||||||
Clio will fallback to hardcoded defaults when not specified in the config file or configured values are outside
|
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`.
|
of the minimum and maximum supported versions hardcoded in `src/rpc/common/APIVersion.h`.
|
||||||
> **Note:** See `example-config.json` for more details.
|
> **Note:** See `example-config.json` for more details.
|
||||||
|
|
||||||
|
|||||||
@@ -111,8 +111,8 @@
|
|||||||
// "ssl_cert_file" : "/full/path/to/cert.file",
|
// "ssl_cert_file" : "/full/path/to/cert.file",
|
||||||
// "ssl_key_file" : "/full/path/to/key.file"
|
// "ssl_key_file" : "/full/path/to/key.file"
|
||||||
"api_version": {
|
"api_version": {
|
||||||
"min": 2,
|
"min": 1, // Minimum API version supported (could be 1 or 2)
|
||||||
"max": 2,
|
"max": 2, // Maximum API version supported (could be 1 or 2, but >= min)
|
||||||
"default": 2 // Clio only supports API v2 and newer
|
"default": 1 // Clio behaves the same as rippled by default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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.
|
* @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 (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"}};
|
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 (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"}};
|
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 (minIndex > maxIndex)
|
||||||
|
{
|
||||||
|
if (ctx.apiVersion == 1u)
|
||||||
|
return Error{Status{RippledError::rpcLGR_IDXS_INVALID}};
|
||||||
|
|
||||||
return Error{Status{RippledError::rpcINVALID_LGR_RANGE}};
|
return Error{Status{RippledError::rpcINVALID_LGR_RANGE}};
|
||||||
|
}
|
||||||
|
|
||||||
if (input.ledgerHash || input.ledgerIndex || input.usingValidatedLedger)
|
if (input.ledgerHash || input.ledgerIndex || input.usingValidatedLedger)
|
||||||
{
|
{
|
||||||
// rippled does not have this check
|
if (ctx.apiVersion > 1u && (input.ledgerIndexMax || input.ledgerIndexMin))
|
||||||
if (input.ledgerIndexMax || input.ledgerIndexMin)
|
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"}};
|
return Error{Status{RippledError::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"}};
|
||||||
|
|
||||||
auto const lgrInfoOrStatus = getLedgerInfoFromHashOrSeq(
|
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)))
|
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)))
|
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)))
|
if (jsonObject.contains(JS(limit)))
|
||||||
input.limit = jsonObject.at(JS(limit)).as_int64();
|
input.limit = jsonObject.at(JS(limit)).as_int64();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <data/BackendInterface.h>
|
#include <data/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/JsonBool.h>
|
||||||
#include <rpc/common/MetaProcessors.h>
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Modifiers.h>
|
#include <rpc/common/Modifiers.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
@@ -76,8 +77,8 @@ public:
|
|||||||
std::optional<int32_t> ledgerIndexMin;
|
std::optional<int32_t> ledgerIndexMin;
|
||||||
std::optional<int32_t> ledgerIndexMax;
|
std::optional<int32_t> ledgerIndexMax;
|
||||||
bool usingValidatedLedger = false;
|
bool usingValidatedLedger = false;
|
||||||
bool binary = false;
|
JsonBool binary{false};
|
||||||
bool forward = false;
|
JsonBool forward{false};
|
||||||
std::optional<uint32_t> limit;
|
std::optional<uint32_t> limit;
|
||||||
std::optional<Marker> marker;
|
std::optional<Marker> marker;
|
||||||
std::optional<ripple::TxType> transactionType;
|
std::optional<ripple::TxType> transactionType;
|
||||||
@@ -92,14 +93,12 @@ public:
|
|||||||
RpcSpecConstRef
|
RpcSpecConstRef
|
||||||
spec([[maybe_unused]] uint32_t apiVersion) const
|
spec([[maybe_unused]] uint32_t apiVersion) const
|
||||||
{
|
{
|
||||||
static auto const rpcSpec = RpcSpec{
|
static auto const rpcSpecForV1 = RpcSpec{
|
||||||
{JS(account), validation::Required{}, validation::AccountValidator},
|
{JS(account), validation::Required{}, validation::AccountValidator},
|
||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
{JS(ledger_index), validation::LedgerIndexValidator},
|
{JS(ledger_index), validation::LedgerIndexValidator},
|
||||||
{JS(ledger_index_min), validation::Type<int32_t>{}},
|
{JS(ledger_index_min), validation::Type<int32_t>{}},
|
||||||
{JS(ledger_index_max), validation::Type<int32_t>{}},
|
{JS(ledger_index_max), validation::Type<int32_t>{}},
|
||||||
{JS(binary), validation::Type<bool>{}},
|
|
||||||
{JS(forward), validation::Type<bool>{}},
|
|
||||||
{JS(limit),
|
{JS(limit),
|
||||||
validation::Type<uint32_t>{},
|
validation::Type<uint32_t>{},
|
||||||
validation::Min(1u),
|
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
|
Result
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ struct AccountTxParamTestCaseBundle
|
|||||||
{
|
{
|
||||||
std::string testName;
|
std::string testName;
|
||||||
std::string testJson;
|
std::string testJson;
|
||||||
std::string expectedError;
|
std::optional<std::string> expectedError;
|
||||||
std::string expectedErrorMessage;
|
std::optional<std::string> expectedErrorMessage;
|
||||||
|
std::uint32_t apiVersion = 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
// parameterized test cases for parameters check
|
// parameterized test cases for parameters check
|
||||||
@@ -58,91 +59,102 @@ struct AccountTxParameterTest : public RPCAccountTxHandlerTest, public WithParam
|
|||||||
std::string
|
std::string
|
||||||
operator()(const testing::TestParamInfo<ParamType>& info) const
|
operator()(const testing::TestParamInfo<ParamType>& info) const
|
||||||
{
|
{
|
||||||
auto bundle = static_cast<AccountTxParamTestCaseBundle>(info.param);
|
return info.param.testName;
|
||||||
return bundle.testName;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
static auto
|
static auto
|
||||||
generateTestValuesForParametersTest()
|
generateTestValuesForParametersTest()
|
||||||
{
|
{
|
||||||
return std::vector<AccountTxParamTestCaseBundle>{
|
return std::vector<AccountTxParamTestCaseBundle>{
|
||||||
AccountTxParamTestCaseBundle{"MissingAccount", R"({})", "invalidParams", "Required field 'account' missing"},
|
AccountTxParamTestCaseBundle{
|
||||||
AccountTxParamTestCaseBundle{
|
"MissingAccount", R"({})", "invalidParams", "Required field 'account' missing"},
|
||||||
"BinaryNotBool",
|
AccountTxParamTestCaseBundle{
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "binary": 1})",
|
"BinaryNotBool",
|
||||||
"invalidParams",
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "binary": 1})",
|
||||||
"Invalid parameters."},
|
"invalidParams",
|
||||||
AccountTxParamTestCaseBundle{
|
"Invalid parameters."},
|
||||||
"ForwardNotBool",
|
AccountTxParamTestCaseBundle{
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "forward": 1})",
|
"BinaryNotBool_API_v1",
|
||||||
"invalidParams",
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "binary": 1})",
|
||||||
"Invalid parameters."},
|
std::nullopt,
|
||||||
AccountTxParamTestCaseBundle{
|
std::nullopt,
|
||||||
"ledger_index_minNotInt",
|
1u},
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": "x"})",
|
AccountTxParamTestCaseBundle{
|
||||||
"invalidParams",
|
"ForwardNotBool",
|
||||||
"Invalid parameters."},
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "forward": 1})",
|
||||||
AccountTxParamTestCaseBundle{
|
"invalidParams",
|
||||||
"ledger_index_maxNotInt",
|
"Invalid parameters."},
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": "x"})",
|
AccountTxParamTestCaseBundle{
|
||||||
"invalidParams",
|
"ForwardNotBool_API_v1",
|
||||||
"Invalid parameters."},
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "forward": 1})",
|
||||||
AccountTxParamTestCaseBundle{
|
std::nullopt,
|
||||||
"ledger_indexInvalid",
|
std::nullopt,
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})",
|
1u},
|
||||||
"invalidParams",
|
AccountTxParamTestCaseBundle{
|
||||||
"ledgerIndexMalformed"},
|
"ledger_index_minNotInt",
|
||||||
AccountTxParamTestCaseBundle{
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_min": "x"})",
|
||||||
"ledger_hashInvalid",
|
"invalidParams",
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})",
|
"Invalid parameters."},
|
||||||
"invalidParams",
|
AccountTxParamTestCaseBundle{
|
||||||
"ledger_hashMalformed"},
|
"ledger_index_maxNotInt",
|
||||||
AccountTxParamTestCaseBundle{
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index_max": "x"})",
|
||||||
"ledger_hashNotString",
|
"invalidParams",
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})",
|
"Invalid parameters."},
|
||||||
"invalidParams",
|
AccountTxParamTestCaseBundle{
|
||||||
"ledger_hashNotString"},
|
"ledger_indexInvalid",
|
||||||
AccountTxParamTestCaseBundle{
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_index": "x"})",
|
||||||
"limitNotInt",
|
"invalidParams",
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "123"})",
|
"ledgerIndexMalformed"},
|
||||||
"invalidParams",
|
AccountTxParamTestCaseBundle{
|
||||||
"Invalid parameters."},
|
"ledger_hashInvalid",
|
||||||
AccountTxParamTestCaseBundle{
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": "x"})",
|
||||||
"limitNegative",
|
"invalidParams",
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})",
|
"ledger_hashMalformed"},
|
||||||
"invalidParams",
|
AccountTxParamTestCaseBundle{
|
||||||
"Invalid parameters."},
|
"ledger_hashNotString",
|
||||||
AccountTxParamTestCaseBundle{
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "ledger_hash": 123})",
|
||||||
"limitZero",
|
"invalidParams",
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})",
|
"ledger_hashNotString"},
|
||||||
"invalidParams",
|
AccountTxParamTestCaseBundle{
|
||||||
"Invalid parameters."},
|
"limitNotInt",
|
||||||
AccountTxParamTestCaseBundle{
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": "123"})",
|
||||||
"MarkerNotObject",
|
"invalidParams",
|
||||||
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 101})",
|
"Invalid parameters."},
|
||||||
"invalidParams",
|
AccountTxParamTestCaseBundle{
|
||||||
"invalidMarker"},
|
"limitNegative",
|
||||||
AccountTxParamTestCaseBundle{
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": -1})",
|
||||||
"MarkerMissingSeq",
|
"invalidParams",
|
||||||
R"({
|
"Invalid parameters."},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"limitZero",
|
||||||
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "limit": 0})",
|
||||||
|
"invalidParams",
|
||||||
|
"Invalid parameters."},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"MarkerNotObject",
|
||||||
|
R"({"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "marker": 101})",
|
||||||
|
"invalidParams",
|
||||||
|
"invalidMarker"},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"MarkerMissingSeq",
|
||||||
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"marker": {"ledger": 123}
|
"marker": {"ledger": 123}
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Required field 'seq' missing"},
|
"Required field 'seq' missing"},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"MarkerMissingLedger",
|
"MarkerMissingLedger",
|
||||||
R"({
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"marker":{"seq": 123}
|
"marker":{"seq": 123}
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Required field 'ledger' missing"},
|
"Required field 'ledger' missing"},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"MarkerLedgerNotInt",
|
"MarkerLedgerNotInt",
|
||||||
R"({
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"marker":
|
"marker":
|
||||||
{
|
{
|
||||||
@@ -150,11 +162,11 @@ generateTestValuesForParametersTest()
|
|||||||
"ledger": 1
|
"ledger": 1
|
||||||
}
|
}
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Invalid parameters."},
|
"Invalid parameters."},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"MarkerSeqNotInt",
|
"MarkerSeqNotInt",
|
||||||
R"({
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"marker":
|
"marker":
|
||||||
{
|
{
|
||||||
@@ -162,77 +174,220 @@ generateTestValuesForParametersTest()
|
|||||||
"seq": 1
|
"seq": 1
|
||||||
}
|
}
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Invalid parameters."},
|
"Invalid parameters."},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"LedgerIndexMinLessThanMinSeq",
|
"LedgerIndexMinLessThanMinSeq",
|
||||||
R"({
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"ledger_index_min": 9
|
"ledger_index_min": 9
|
||||||
})",
|
})",
|
||||||
"lgrIdxMalformed",
|
"lgrIdxMalformed",
|
||||||
"ledgerSeqMinOutOfRange"},
|
"ledgerSeqMinOutOfRange"},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"LedgerIndexMaxLargeThanMaxSeq",
|
"LedgerIndexMaxLargeThanMaxSeq",
|
||||||
R"({
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"ledger_index_max": 31
|
"ledger_index_max": 31
|
||||||
})",
|
})",
|
||||||
"lgrIdxMalformed",
|
"lgrIdxMalformed",
|
||||||
"ledgerSeqMaxOutOfRange"},
|
"ledgerSeqMaxOutOfRange"},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"LedgerIndexMaxLessThanLedgerIndexMin",
|
"LedgerIndexMaxLargeThanMaxSeq_API_v1",
|
||||||
R"({
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 31
|
||||||
|
})",
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
1u},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxSmallerThanMinSeq",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 9
|
||||||
|
})",
|
||||||
|
"lgrIdxMalformed",
|
||||||
|
"ledgerSeqMaxOutOfRange"},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxSmallerThanMinSeq_API_v1",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 9
|
||||||
|
})",
|
||||||
|
"lgrIdxsInvalid",
|
||||||
|
"Ledger indexes invalid.",
|
||||||
|
1u},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMinSmallerThanMinSeq",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_min": 9
|
||||||
|
})",
|
||||||
|
"lgrIdxMalformed",
|
||||||
|
"ledgerSeqMinOutOfRange"},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMinSmallerThanMinSeq_API_v1",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_min": 9
|
||||||
|
})",
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
1u},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMinLargerThanMaxSeq",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_min": 31
|
||||||
|
})",
|
||||||
|
"lgrIdxMalformed",
|
||||||
|
"ledgerSeqMinOutOfRange"},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMinLargerThanMaxSeq_API_v1",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_min": 31
|
||||||
|
})",
|
||||||
|
"lgrIdxsInvalid",
|
||||||
|
"Ledger indexes invalid.",
|
||||||
|
1u},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxLessThanLedgerIndexMin",
|
||||||
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"ledger_index_max": 11,
|
"ledger_index_max": 11,
|
||||||
"ledger_index_min": 20
|
"ledger_index_min": 20
|
||||||
})",
|
})",
|
||||||
"invalidLgrRange",
|
"invalidLgrRange",
|
||||||
"Ledger range is invalid."},
|
"Ledger range is invalid."},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"LedgerIndexMaxMinAndLedgerIndex",
|
"LedgerIndexMaxLessThanLedgerIndexMin_API_v1",
|
||||||
R"({
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 11,
|
||||||
|
"ledger_index_min": 20
|
||||||
|
})",
|
||||||
|
"lgrIdxsInvalid",
|
||||||
|
"Ledger indexes invalid.",
|
||||||
|
1u},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxMinAndLedgerIndex",
|
||||||
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"ledger_index_max": 20,
|
"ledger_index_max": 20,
|
||||||
"ledger_index_min": 11,
|
"ledger_index_min": 11,
|
||||||
"ledger_index": 10
|
"ledger_index": 10
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"containsLedgerSpecifierAndRange"},
|
"containsLedgerSpecifierAndRange"},
|
||||||
AccountTxParamTestCaseBundle{
|
AccountTxParamTestCaseBundle{
|
||||||
"LedgerIndexMaxMinAndLedgerIndexValidated",
|
"LedgerIndexMaxMinAndLedgerIndexValidated",
|
||||||
R"({
|
R"({
|
||||||
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"ledger_index_max": 20,
|
"ledger_index_max": 20,
|
||||||
"ledger_index_min": 11,
|
"ledger_index_min": 11,
|
||||||
"ledger_index": "validated"
|
"ledger_index": "validated"
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"containsLedgerSpecifierAndRange"},
|
"containsLedgerSpecifierAndRange"},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxMinAndLedgerIndex_API_v1",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 20,
|
||||||
|
"ledger_index_min": 11,
|
||||||
|
"ledger_index": 10
|
||||||
|
})",
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
1u},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxMinAndLedgerHash",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 20,
|
||||||
|
"ledger_index_min": 11,
|
||||||
|
"ledger_hash": "{}"
|
||||||
|
}})",
|
||||||
|
LEDGERHASH),
|
||||||
|
"invalidParams",
|
||||||
|
"containsLedgerSpecifierAndRange"},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxMinAndLedgerHash_API_v1",
|
||||||
|
fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 20,
|
||||||
|
"ledger_index_min": 11,
|
||||||
|
"ledger_hash": "{}"
|
||||||
|
}})",
|
||||||
|
LEDGERHASH),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
1u},
|
||||||
|
AccountTxParamTestCaseBundle{
|
||||||
|
"LedgerIndexMaxMinAndLedgerIndexValidated_API_v1",
|
||||||
|
R"({
|
||||||
|
"account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_max": 20,
|
||||||
|
"ledger_index_min": 11,
|
||||||
|
"ledger_index": "validated"
|
||||||
|
})",
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
1u},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
INSTANTIATE_TEST_CASE_P(
|
||||||
RPCAccountTxGroup1,
|
RPCAccountTxGroup1,
|
||||||
AccountTxParameterTest,
|
AccountTxParameterTest,
|
||||||
ValuesIn(generateTestValuesForParametersTest()),
|
ValuesIn(AccountTxParameterTest::generateTestValuesForParametersTest()),
|
||||||
AccountTxParameterTest::NameGenerator{});
|
AccountTxParameterTest::NameGenerator{});
|
||||||
|
|
||||||
TEST_P(AccountTxParameterTest, InvalidParams)
|
TEST_P(AccountTxParameterTest, CheckParams)
|
||||||
{
|
{
|
||||||
mockBackendPtr->updateRange(MINSEQ); // min
|
mockBackendPtr->updateRange(MINSEQ); // min
|
||||||
mockBackendPtr->updateRange(MAXSEQ); // max
|
mockBackendPtr->updateRange(MAXSEQ); // max
|
||||||
auto const testBundle = GetParam();
|
auto const testBundle = GetParam();
|
||||||
runSpawn([&, this](auto yield) {
|
auto* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
std::cout << "Before parse" << std::endl;
|
||||||
auto const req = json::parse(testBundle.testJson);
|
auto const req = json::parse(testBundle.testJson);
|
||||||
auto const output = handler.process(req, Context{yield});
|
std::cout << "After parse" << std::endl;
|
||||||
ASSERT_FALSE(output);
|
if (testBundle.expectedError.has_value())
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(testBundle.expectedErrorMessage.has_value());
|
||||||
|
|
||||||
auto const err = rpc::makeError(output.error());
|
runSpawn([&, this](auto yield) {
|
||||||
EXPECT_EQ(err.at("error").as_string(), testBundle.expectedError);
|
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||||
EXPECT_EQ(err.at("error_message").as_string(), testBundle.expectedErrorMessage);
|
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = testBundle.apiVersion});
|
||||||
});
|
ASSERT_FALSE(output);
|
||||||
|
auto const err = rpc::makeError(output.error());
|
||||||
|
EXPECT_EQ(err.at("error").as_string(), *testBundle.expectedError);
|
||||||
|
EXPECT_EQ(err.at("error_message").as_string(), *testBundle.expectedErrorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (req.as_object().contains("ledger_hash"))
|
||||||
|
{
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).WillOnce(testing::Return(ripple::LedgerHeader{}));
|
||||||
|
}
|
||||||
|
else if (req.as_object().contains("ledger_index"))
|
||||||
|
{
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).WillOnce(testing::Return(ripple::LedgerHeader{}));
|
||||||
|
}
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchAccountTransactions);
|
||||||
|
|
||||||
|
runSpawn([&, this](auto yield) {
|
||||||
|
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||||
|
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = testBundle.apiVersion});
|
||||||
|
EXPECT_TRUE(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|||||||
Reference in New Issue
Block a user