mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Refactor ledger_entry RPC source code and tests (#5237)
This is a major refactor of LedgerEntry.cpp. It adds a number of helper functions to make the code easier to maintain. It also splits up the ledger and ledger_entry tests into different files, and cleans up the ledger_entry tests to make them easier to write and maintain. This refactor also caught a few bugs in some of the other RPC processing, so those are fixed along the way.
This commit is contained in:
@@ -157,7 +157,12 @@ enum error_code_i {
|
|||||||
// Pathfinding
|
// Pathfinding
|
||||||
rpcDOMAIN_MALFORMED = 97,
|
rpcDOMAIN_MALFORMED = 97,
|
||||||
|
|
||||||
rpcLAST = rpcDOMAIN_MALFORMED // rpcLAST should always equal the last code.
|
// ledger_entry
|
||||||
|
rpcENTRY_NOT_FOUND = 98,
|
||||||
|
rpcUNEXPECTED_LEDGER_TYPE = 99,
|
||||||
|
|
||||||
|
rpcLAST =
|
||||||
|
rpcUNEXPECTED_LEDGER_TYPE // rpcLAST should always equal the last code.
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Codes returned in the `warnings` array of certain RPC commands.
|
/** Codes returned in the `warnings` array of certain RPC commands.
|
||||||
|
|||||||
@@ -68,9 +68,13 @@ JSS(Flags); // in/out: TransactionSign; field.
|
|||||||
JSS(Holder); // field.
|
JSS(Holder); // field.
|
||||||
JSS(Invalid); //
|
JSS(Invalid); //
|
||||||
JSS(Issuer); // in: Credential transactions
|
JSS(Issuer); // in: Credential transactions
|
||||||
|
JSS(IssuingChainDoor); // field.
|
||||||
|
JSS(IssuingChainIssue); // field.
|
||||||
JSS(LastLedgerSequence); // in: TransactionSign; field
|
JSS(LastLedgerSequence); // in: TransactionSign; field
|
||||||
JSS(LastUpdateTime); // field.
|
JSS(LastUpdateTime); // field.
|
||||||
JSS(LimitAmount); // field.
|
JSS(LimitAmount); // field.
|
||||||
|
JSS(LockingChainDoor); // field.
|
||||||
|
JSS(LockingChainIssue); // field.
|
||||||
JSS(NetworkID); // field.
|
JSS(NetworkID); // field.
|
||||||
JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens
|
JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens
|
||||||
JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens
|
JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
#include <xrpl/json/json_writer.h>
|
#include <xrpl/json/json_writer.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -685,7 +686,9 @@ Value::isConvertibleTo(ValueType other) const
|
|||||||
(other == intValue && value_.real_ >= minInt &&
|
(other == intValue && value_.real_ >= minInt &&
|
||||||
value_.real_ <= maxInt) ||
|
value_.real_ <= maxInt) ||
|
||||||
(other == uintValue && value_.real_ >= 0 &&
|
(other == uintValue && value_.real_ >= 0 &&
|
||||||
value_.real_ <= maxUInt) ||
|
value_.real_ <= maxUInt &&
|
||||||
|
std::fabs(round(value_.real_) - value_.real_) <
|
||||||
|
std::numeric_limits<double>::epsilon()) ||
|
||||||
other == realValue || other == stringValue ||
|
other == realValue || other == stringValue ||
|
||||||
other == booleanValue;
|
other == booleanValue;
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,10 @@ constexpr static ErrorInfo unorderedErrorInfos[]{
|
|||||||
{rpcORACLE_MALFORMED, "oracleMalformed", "Oracle request is malformed.", 400},
|
{rpcORACLE_MALFORMED, "oracleMalformed", "Oracle request is malformed.", 400},
|
||||||
{rpcBAD_CREDENTIALS, "badCredentials", "Credentials do not exist, are not accepted, or have expired.", 400},
|
{rpcBAD_CREDENTIALS, "badCredentials", "Credentials do not exist, are not accepted, or have expired.", 400},
|
||||||
{rpcTX_SIGNED, "transactionSigned", "Transaction should not be signed.", 400},
|
{rpcTX_SIGNED, "transactionSigned", "Transaction should not be signed.", 400},
|
||||||
{rpcDOMAIN_MALFORMED, "domainMalformed", "Domain is malformed.", 400}};
|
{rpcDOMAIN_MALFORMED, "domainMalformed", "Domain is malformed.", 400},
|
||||||
|
{rpcENTRY_NOT_FOUND, "entryNotFound", "Entry not found.", 400},
|
||||||
|
{rpcUNEXPECTED_LEDGER_TYPE, "unexpectedLedgerType", "Unexpected ledger type.", 400},
|
||||||
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// Sort and validate unorderedErrorInfos at compile time. Should be
|
// Sort and validate unorderedErrorInfos at compile time. Should be
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <xrpl/protocol/STObject.h>
|
#include <xrpl/protocol/STObject.h>
|
||||||
#include <xrpl/protocol/STXChainBridge.h>
|
#include <xrpl/protocol/STXChainBridge.h>
|
||||||
#include <xrpl/protocol/Serializer.h>
|
#include <xrpl/protocol/Serializer.h>
|
||||||
|
#include <xrpl/protocol/jss.h>
|
||||||
|
|
||||||
#include <boost/format/free_funcs.hpp>
|
#include <boost/format/free_funcs.hpp>
|
||||||
|
|
||||||
@@ -98,12 +99,10 @@ STXChainBridge::STXChainBridge(SField const& name, Json::Value const& v)
|
|||||||
};
|
};
|
||||||
checkExtra(v);
|
checkExtra(v);
|
||||||
|
|
||||||
Json::Value const& lockingChainDoorStr =
|
Json::Value const& lockingChainDoorStr = v[jss::LockingChainDoor];
|
||||||
v[sfLockingChainDoor.getJsonName()];
|
Json::Value const& lockingChainIssue = v[jss::LockingChainIssue];
|
||||||
Json::Value const& lockingChainIssue = v[sfLockingChainIssue.getJsonName()];
|
Json::Value const& issuingChainDoorStr = v[jss::IssuingChainDoor];
|
||||||
Json::Value const& issuingChainDoorStr =
|
Json::Value const& issuingChainIssue = v[jss::IssuingChainIssue];
|
||||||
v[sfIssuingChainDoor.getJsonName()];
|
|
||||||
Json::Value const& issuingChainIssue = v[sfIssuingChainIssue.getJsonName()];
|
|
||||||
|
|
||||||
if (!lockingChainDoorStr.isString())
|
if (!lockingChainDoorStr.isString())
|
||||||
{
|
{
|
||||||
@@ -161,10 +160,10 @@ Json::Value
|
|||||||
STXChainBridge::getJson(JsonOptions jo) const
|
STXChainBridge::getJson(JsonOptions jo) const
|
||||||
{
|
{
|
||||||
Json::Value v;
|
Json::Value v;
|
||||||
v[sfLockingChainDoor.getJsonName()] = lockingChainDoor_.getJson(jo);
|
v[jss::LockingChainDoor] = lockingChainDoor_.getJson(jo);
|
||||||
v[sfLockingChainIssue.getJsonName()] = lockingChainIssue_.getJson(jo);
|
v[jss::LockingChainIssue] = lockingChainIssue_.getJson(jo);
|
||||||
v[sfIssuingChainDoor.getJsonName()] = issuingChainDoor_.getJson(jo);
|
v[jss::IssuingChainDoor] = issuingChainDoor_.getJson(jo);
|
||||||
v[sfIssuingChainIssue.getJsonName()] = issuingChainIssue_.getJson(jo);
|
v[jss::IssuingChainIssue] = issuingChainIssue_.getJson(jo);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3028,18 +3028,6 @@ class Vault_test : public beast::unit_test::suite
|
|||||||
"malformedRequest");
|
"malformedRequest");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
testcase("RPC ledger_entry zero seq");
|
|
||||||
Json::Value jvParams;
|
|
||||||
jvParams[jss::ledger_index] = jss::validated;
|
|
||||||
jvParams[jss::vault][jss::owner] = issuer.human();
|
|
||||||
jvParams[jss::vault][jss::seq] = 0;
|
|
||||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
|
||||||
BEAST_EXPECT(
|
|
||||||
jvVault[jss::result][jss::error].asString() ==
|
|
||||||
"malformedRequest");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
testcase("RPC ledger_entry negative seq");
|
testcase("RPC ledger_entry negative seq");
|
||||||
Json::Value jvParams;
|
Json::Value jvParams;
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ bridge(
|
|||||||
Issue const& issuingChainIssue)
|
Issue const& issuingChainIssue)
|
||||||
{
|
{
|
||||||
Json::Value jv;
|
Json::Value jv;
|
||||||
jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human();
|
jv[jss::LockingChainDoor] = lockingChainDoor.human();
|
||||||
jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue);
|
jv[jss::LockingChainIssue] = to_json(lockingChainIssue);
|
||||||
jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human();
|
jv[jss::IssuingChainDoor] = issuingChainDoor.human();
|
||||||
jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue);
|
jv[jss::IssuingChainIssue] = to_json(issuingChainIssue);
|
||||||
return jv;
|
return jv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,10 +60,10 @@ bridge_rpc(
|
|||||||
Issue const& issuingChainIssue)
|
Issue const& issuingChainIssue)
|
||||||
{
|
{
|
||||||
Json::Value jv;
|
Json::Value jv;
|
||||||
jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human();
|
jv[jss::LockingChainDoor] = lockingChainDoor.human();
|
||||||
jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue);
|
jv[jss::LockingChainIssue] = to_json(lockingChainIssue);
|
||||||
jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human();
|
jv[jss::IssuingChainDoor] = issuingChainDoor.human();
|
||||||
jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue);
|
jv[jss::IssuingChainIssue] = to_json(issuingChainIssue);
|
||||||
return jv;
|
return jv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -190,7 +190,7 @@ getAccountObjects(
|
|||||||
|
|
||||||
auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue);
|
auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue);
|
||||||
|
|
||||||
// this is a mutable version of limit, used to seemlessly switch
|
// this is a mutable version of limit, used to seamlessly switch
|
||||||
// to iterating directory entries when nftokenpages are exhausted
|
// to iterating directory entries when nftokenpages are exhausted
|
||||||
uint32_t mlimit = limit;
|
uint32_t mlimit = limit;
|
||||||
|
|
||||||
@@ -373,7 +373,7 @@ ledgerFromRequest(T& ledger, JsonContext& context)
|
|||||||
indexValue = legacyLedger;
|
indexValue = legacyLedger;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashValue)
|
if (!hashValue.isNull())
|
||||||
{
|
{
|
||||||
if (!hashValue.isString())
|
if (!hashValue.isString())
|
||||||
return {rpcINVALID_PARAMS, "ledgerHashNotString"};
|
return {rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||||
@@ -384,6 +384,9 @@ ledgerFromRequest(T& ledger, JsonContext& context)
|
|||||||
return getLedger(ledger, ledgerHash, context);
|
return getLedger(ledger, ledgerHash, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!indexValue.isConvertibleTo(Json::stringValue))
|
||||||
|
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||||
|
|
||||||
auto const index = indexValue.asString();
|
auto const index = indexValue.asString();
|
||||||
|
|
||||||
if (index == "current" || index.empty())
|
if (index == "current" || index.empty())
|
||||||
@@ -395,11 +398,11 @@ ledgerFromRequest(T& ledger, JsonContext& context)
|
|||||||
if (index == "closed")
|
if (index == "closed")
|
||||||
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
||||||
|
|
||||||
std::uint32_t iVal;
|
std::uint32_t val;
|
||||||
if (beast::lexicalCastChecked(iVal, index))
|
if (!beast::lexicalCastChecked(val, index))
|
||||||
return getLedger(ledger, iVal, context);
|
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||||
|
|
||||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
return getLedger(ledger, val, context);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -586,7 +589,7 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context& context)
|
|||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit instantiaion of above three functions
|
// Explicit instantiation of above three functions
|
||||||
template Status
|
template Status
|
||||||
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context&);
|
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context&);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
299
src/xrpld/rpc/handlers/LedgerEntryHelpers.h
Normal file
299
src/xrpld/rpc/handlers/LedgerEntryHelpers.h
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012-2025 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
|
||||||
|
#include <xrpl/basics/StringUtilities.h>
|
||||||
|
#include <xrpl/basics/strHex.h>
|
||||||
|
#include <xrpl/beast/core/LexicalCast.h>
|
||||||
|
#include <xrpl/json/json_errors.h>
|
||||||
|
#include <xrpl/protocol/ErrorCodes.h>
|
||||||
|
#include <xrpl/protocol/Indexes.h>
|
||||||
|
#include <xrpl/protocol/RPCErr.h>
|
||||||
|
#include <xrpl/protocol/STXChainBridge.h>
|
||||||
|
#include <xrpl/protocol/jss.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
namespace LedgerEntryHelpers {
|
||||||
|
|
||||||
|
Unexpected<Json::Value>
|
||||||
|
missingFieldError(
|
||||||
|
Json::StaticString const field,
|
||||||
|
std::optional<std::string> err = std::nullopt)
|
||||||
|
{
|
||||||
|
Json::Value json = Json::objectValue;
|
||||||
|
auto error = RPC::missing_field_message(std::string(field.c_str()));
|
||||||
|
json[jss::error] = err.value_or("malformedRequest");
|
||||||
|
json[jss::error_code] = rpcINVALID_PARAMS;
|
||||||
|
json[jss::error_message] = std::move(error);
|
||||||
|
return Unexpected(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unexpected<Json::Value>
|
||||||
|
invalidFieldError(
|
||||||
|
std::string const& err,
|
||||||
|
Json::StaticString const field,
|
||||||
|
std::string const& type)
|
||||||
|
{
|
||||||
|
Json::Value json = Json::objectValue;
|
||||||
|
auto error = RPC::expected_field_message(field, type);
|
||||||
|
json[jss::error] = err;
|
||||||
|
json[jss::error_code] = rpcINVALID_PARAMS;
|
||||||
|
json[jss::error_message] = std::move(error);
|
||||||
|
return Unexpected(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unexpected<Json::Value>
|
||||||
|
malformedError(std::string const& err, std::string const& message)
|
||||||
|
{
|
||||||
|
Json::Value json = Json::objectValue;
|
||||||
|
json[jss::error] = err;
|
||||||
|
json[jss::error_code] = rpcINVALID_PARAMS;
|
||||||
|
json[jss::error_message] = message;
|
||||||
|
return Unexpected(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<bool, Json::Value>
|
||||||
|
hasRequired(
|
||||||
|
Json::Value const& params,
|
||||||
|
std::initializer_list<Json::StaticString> fields,
|
||||||
|
std::optional<std::string> err = std::nullopt)
|
||||||
|
{
|
||||||
|
for (auto const field : fields)
|
||||||
|
{
|
||||||
|
if (!params.isMember(field) || params[field].isNull())
|
||||||
|
{
|
||||||
|
return missingFieldError(field, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::optional<T>
|
||||||
|
parse(Json::Value const& param);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Expected<T, Json::Value>
|
||||||
|
required(
|
||||||
|
Json::Value const& params,
|
||||||
|
Json::StaticString const fieldName,
|
||||||
|
std::string const& err,
|
||||||
|
std::string const& expectedType)
|
||||||
|
{
|
||||||
|
if (!params.isMember(fieldName) || params[fieldName].isNull())
|
||||||
|
{
|
||||||
|
return missingFieldError(fieldName);
|
||||||
|
}
|
||||||
|
if (auto obj = parse<T>(params[fieldName]))
|
||||||
|
{
|
||||||
|
return *obj;
|
||||||
|
}
|
||||||
|
return invalidFieldError(err, fieldName, expectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::optional<AccountID>
|
||||||
|
parse(Json::Value const& param)
|
||||||
|
{
|
||||||
|
if (!param.isString())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto const account = parseBase58<AccountID>(param.asString());
|
||||||
|
if (!account || account->isZero())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<AccountID, Json::Value>
|
||||||
|
requiredAccountID(
|
||||||
|
Json::Value const& params,
|
||||||
|
Json::StaticString const fieldName,
|
||||||
|
std::string const& err)
|
||||||
|
{
|
||||||
|
return required<AccountID>(params, fieldName, err, "AccountID");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Blob>
|
||||||
|
parseHexBlob(Json::Value const& param, std::size_t maxLength)
|
||||||
|
{
|
||||||
|
if (!param.isString())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto const blob = strUnHex(param.asString());
|
||||||
|
if (!blob || blob->empty() || blob->size() > maxLength)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<Blob, Json::Value>
|
||||||
|
requiredHexBlob(
|
||||||
|
Json::Value const& params,
|
||||||
|
Json::StaticString const fieldName,
|
||||||
|
std::size_t maxLength,
|
||||||
|
std::string const& err)
|
||||||
|
{
|
||||||
|
if (!params.isMember(fieldName) || params[fieldName].isNull())
|
||||||
|
{
|
||||||
|
return missingFieldError(fieldName);
|
||||||
|
}
|
||||||
|
if (auto blob = parseHexBlob(params[fieldName], maxLength))
|
||||||
|
{
|
||||||
|
return *blob;
|
||||||
|
}
|
||||||
|
return invalidFieldError(err, fieldName, "hex string");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::optional<std::uint32_t>
|
||||||
|
parse(Json::Value const& param)
|
||||||
|
{
|
||||||
|
if (param.isUInt() || (param.isInt() && param.asInt() >= 0))
|
||||||
|
return param.asUInt();
|
||||||
|
|
||||||
|
if (param.isString())
|
||||||
|
{
|
||||||
|
std::uint32_t v;
|
||||||
|
if (beast::lexicalCastChecked(v, param.asString()))
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<std::uint32_t, Json::Value>
|
||||||
|
requiredUInt32(
|
||||||
|
Json::Value const& params,
|
||||||
|
Json::StaticString const fieldName,
|
||||||
|
std::string const& err)
|
||||||
|
{
|
||||||
|
return required<std::uint32_t>(params, fieldName, err, "number");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::optional<uint256>
|
||||||
|
parse(Json::Value const& param)
|
||||||
|
{
|
||||||
|
uint256 uNodeIndex;
|
||||||
|
if (!param.isString() || !uNodeIndex.parseHex(param.asString()))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uNodeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<uint256, Json::Value>
|
||||||
|
requiredUInt256(
|
||||||
|
Json::Value const& params,
|
||||||
|
Json::StaticString const fieldName,
|
||||||
|
std::string const& err)
|
||||||
|
{
|
||||||
|
return required<uint256>(params, fieldName, err, "Hash256");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::optional<uint192>
|
||||||
|
parse(Json::Value const& param)
|
||||||
|
{
|
||||||
|
uint192 field;
|
||||||
|
if (!param.isString() || !field.parseHex(param.asString()))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<uint192, Json::Value>
|
||||||
|
requiredUInt192(
|
||||||
|
Json::Value const& params,
|
||||||
|
Json::StaticString const fieldName,
|
||||||
|
std::string const& err)
|
||||||
|
{
|
||||||
|
return required<uint192>(params, fieldName, err, "Hash192");
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<STXChainBridge, Json::Value>
|
||||||
|
parseBridgeFields(Json::Value const& params)
|
||||||
|
{
|
||||||
|
if (auto const value = hasRequired(
|
||||||
|
params,
|
||||||
|
{jss::LockingChainDoor,
|
||||||
|
jss::LockingChainIssue,
|
||||||
|
jss::IssuingChainDoor,
|
||||||
|
jss::IssuingChainIssue});
|
||||||
|
!value)
|
||||||
|
{
|
||||||
|
return Unexpected(value.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const lockingChainDoor = requiredAccountID(
|
||||||
|
params, jss::LockingChainDoor, "malformedLockingChainDoor");
|
||||||
|
if (!lockingChainDoor)
|
||||||
|
{
|
||||||
|
return Unexpected(lockingChainDoor.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const issuingChainDoor = requiredAccountID(
|
||||||
|
params, jss::IssuingChainDoor, "malformedIssuingChainDoor");
|
||||||
|
if (!issuingChainDoor)
|
||||||
|
{
|
||||||
|
return Unexpected(issuingChainDoor.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Issue lockingChainIssue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lockingChainIssue = issueFromJson(params[jss::LockingChainIssue]);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& ex)
|
||||||
|
{
|
||||||
|
return invalidFieldError(
|
||||||
|
"malformedIssue", jss::LockingChainIssue, "Issue");
|
||||||
|
}
|
||||||
|
|
||||||
|
Issue issuingChainIssue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
issuingChainIssue = issueFromJson(params[jss::IssuingChainIssue]);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error const& ex)
|
||||||
|
{
|
||||||
|
return invalidFieldError(
|
||||||
|
"malformedIssue", jss::IssuingChainIssue, "Issue");
|
||||||
|
}
|
||||||
|
|
||||||
|
return STXChainBridge(
|
||||||
|
*lockingChainDoor,
|
||||||
|
lockingChainIssue,
|
||||||
|
*issuingChainDoor,
|
||||||
|
issuingChainIssue);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace LedgerEntryHelpers
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
Reference in New Issue
Block a user