mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
588 lines
20 KiB
C++
588 lines
20 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012-2014 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.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#define MAGIC_ENUM_NO_CHECK_REFLECTED_ENUM
|
|
|
|
#include <xrpld/app/main/Application.h>
|
|
#include <xrpld/app/misc/AmendmentTable.h>
|
|
#include <xrpld/app/misc/NetworkOPs.h>
|
|
#include <xrpld/rpc/detail/TransactionSign.h>
|
|
#include <xrpl/json/json_value.h>
|
|
#include <xrpl/json/json_writer.h>
|
|
#include <xrpl/protocol/LedgerFormats.h>
|
|
#include <xrpl/protocol/RPCErr.h>
|
|
#include <xrpl/protocol/SField.h>
|
|
#include <xrpl/protocol/TxFlags.h>
|
|
#include <xrpl/protocol/digest.h>
|
|
#include <xrpl/protocol/jss.h>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <magic/magic_enum.h>
|
|
#include <sstream>
|
|
|
|
#define MAGIC_ENUM(x, _min, _max) \
|
|
template <> \
|
|
struct magic_enum::customize::enum_range<x> \
|
|
{ \
|
|
static constexpr int min = _min; \
|
|
static constexpr int max = _max; \
|
|
};
|
|
|
|
#define MAGIC_ENUM_16(x) \
|
|
template <> \
|
|
struct magic_enum::customize::enum_range<x> \
|
|
{ \
|
|
static constexpr int min = -128; \
|
|
static constexpr int max = 127; \
|
|
};
|
|
|
|
#define MAGIC_ENUM_FLAG(x) \
|
|
template <> \
|
|
struct magic_enum::customize::enum_range<x> \
|
|
{ \
|
|
static constexpr bool is_flags = true; \
|
|
};
|
|
|
|
MAGIC_ENUM(ripple::SerializedTypeID, -2, 10004);
|
|
MAGIC_ENUM(ripple::LedgerEntryType, 0, 255);
|
|
MAGIC_ENUM(ripple::TELcodes, -399, 300);
|
|
MAGIC_ENUM(ripple::TEMcodes, -299, -200);
|
|
MAGIC_ENUM(ripple::TEFcodes, -199, -100);
|
|
MAGIC_ENUM(ripple::TERcodes, -99, -1);
|
|
MAGIC_ENUM(ripple::TEScodes, 0, 1);
|
|
MAGIC_ENUM(ripple::TECcodes, 100, 255);
|
|
MAGIC_ENUM_16(ripple::TxType);
|
|
MAGIC_ENUM_FLAG(ripple::UniversalFlags);
|
|
MAGIC_ENUM_FLAG(ripple::AccountSetFlags);
|
|
MAGIC_ENUM_FLAG(ripple::OfferCreateFlags);
|
|
MAGIC_ENUM_FLAG(ripple::PaymentFlags);
|
|
MAGIC_ENUM_FLAG(ripple::TrustSetFlags);
|
|
MAGIC_ENUM_FLAG(ripple::EnableAmendmentFlags);
|
|
MAGIC_ENUM_FLAG(ripple::PaymentChannelClaimFlags);
|
|
MAGIC_ENUM_FLAG(ripple::NFTokenMintFlags);
|
|
MAGIC_ENUM_FLAG(ripple::NFTokenCreateOfferFlags);
|
|
MAGIC_ENUM_FLAG(ripple::ClaimRewardFlags);
|
|
MAGIC_ENUM_16(ripple::AccountFlags);
|
|
|
|
namespace ripple {
|
|
|
|
class Definitions
|
|
{
|
|
private:
|
|
#define STR(x) \
|
|
([&] { \
|
|
std::ostringstream ss; \
|
|
return ss << (x), ss.str(); \
|
|
}())
|
|
|
|
template <typename EnumType>
|
|
void
|
|
addFlagsToJson(Json::Value& json, std::string const& key)
|
|
{
|
|
for (auto const& entry : magic_enum::enum_entries<EnumType>())
|
|
{
|
|
const auto name = entry.second;
|
|
json[jss::TRANSACTION_FLAGS][key][STR(name)] =
|
|
static_cast<uint32_t>(entry.first);
|
|
}
|
|
}
|
|
|
|
Json::Value
|
|
generate()
|
|
{
|
|
Json::Value ret{Json::objectValue};
|
|
ret[jss::TYPES] = Json::objectValue;
|
|
|
|
auto const translate = [](std::string inp) -> std::string {
|
|
auto replace = [&](const char* f, const char* r) -> std::string {
|
|
std::string out = inp;
|
|
boost::replace_all(out, f, r);
|
|
return out;
|
|
};
|
|
|
|
auto find = [&](const char* s) -> bool {
|
|
return inp.find(s) != std::string::npos;
|
|
};
|
|
|
|
if (find("UINT"))
|
|
{
|
|
if (find("512") || find("384") || find("256") || find("192") || find("160") || find("128"))
|
|
return replace("UINT", "Hash");
|
|
else
|
|
return replace("UINT", "UInt");
|
|
}
|
|
|
|
if (inp == "OBJECT")
|
|
return "STObject";
|
|
if (inp == "ARRAY")
|
|
return "STArray";
|
|
if (inp == "AMM")
|
|
return "AMM";
|
|
if (inp == "ACCOUNT")
|
|
return "AccountID";
|
|
if (inp == "LEDGERENTRY")
|
|
return "LedgerEntry";
|
|
if (inp == "NOTPRESENT")
|
|
return "NotPresent";
|
|
if (inp == "PATHSET")
|
|
return "PathSet";
|
|
if (inp == "VL")
|
|
return "Blob";
|
|
if (inp == "DIR_NODE")
|
|
return "DirectoryNode";
|
|
if (inp == "PAYCHAN")
|
|
return "PayChannel";
|
|
if (inp == "IMPORT_VLSEQ")
|
|
return "ImportVLSequence";
|
|
|
|
static const std::map<std::string, std::string>
|
|
capitalization_exceptions = {
|
|
{"NFTOKEN", "NFToken"},
|
|
{"UNL", "UNL"},
|
|
{"XCHAIN", "XChain"},
|
|
{"ID", "ID"},
|
|
{"AMM", "AMM"},
|
|
{"URITOKEN", "URIToken"},
|
|
{"URI", "URI"},
|
|
{"DID", "DID"},
|
|
{"MPTOKEN", "MPToken"}};
|
|
|
|
std::string out;
|
|
size_t pos = 0;
|
|
for (;;)
|
|
{
|
|
pos = inp.find("_");
|
|
if (pos == std::string::npos)
|
|
pos = inp.size();
|
|
std::string token = inp.substr(0, pos);
|
|
if (auto const e = capitalization_exceptions.find(token);
|
|
e != capitalization_exceptions.end())
|
|
out += e->second;
|
|
else if (token.size() > 1)
|
|
{
|
|
boost::algorithm::to_lower(token);
|
|
token.data()[0] -= ('a' - 'A');
|
|
out += token;
|
|
}
|
|
else
|
|
out += token;
|
|
if (pos == inp.size())
|
|
break;
|
|
inp = inp.substr(pos + 1);
|
|
}
|
|
return out;
|
|
};
|
|
|
|
ret[jss::TYPES]["Done"] = -1;
|
|
std::map<int32_t, std::string> type_map{{-1, "Done"}};
|
|
for (auto const& entry : magic_enum::enum_entries<SerializedTypeID>())
|
|
{
|
|
const auto name = entry.second;
|
|
std::string type_name =
|
|
translate(name.data() + 4 /* remove STI_ */);
|
|
int32_t type_value = static_cast<int32_t>(entry.first);
|
|
ret[jss::TYPES][type_name] = type_value;
|
|
type_map[type_value] = type_name;
|
|
}
|
|
|
|
ret[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
|
|
ret[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
|
|
for (auto const& entry : magic_enum::enum_entries<LedgerEntryType>())
|
|
{
|
|
const auto name = entry.second;
|
|
std::string type_name = translate(name.data() + 2 /* remove lt_ */);
|
|
int32_t type_value = static_cast<int32_t>(entry.first);
|
|
ret[jss::LEDGER_ENTRY_TYPES][type_name] = type_value;
|
|
}
|
|
|
|
ret[jss::FIELDS] = Json::arrayValue;
|
|
|
|
uint32_t i = 0;
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "Generic";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = 0;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = false;
|
|
v[jss::type] = "Unknown";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "Invalid";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = -1;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = false;
|
|
v[jss::type] = "Unknown";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "ObjectEndMarker";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = 1;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = true;
|
|
v[jss::type] = "STObject";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "ArrayEndMarker";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = 1;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = true;
|
|
v[jss::type] = "STArray";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "hash";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = 257;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = false;
|
|
v[jss::type] = "Hash256";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "index";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = 258;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = false;
|
|
v[jss::type] = "Hash256";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "taker_gets_funded";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = 258;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = false;
|
|
v[jss::type] = "Amount";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
{
|
|
Json::Value a = Json::arrayValue;
|
|
a[0U] = "taker_pays_funded";
|
|
Json::Value v = Json::objectValue;
|
|
v[jss::nth] = 259;
|
|
v[jss::isVLEncoded] = false;
|
|
v[jss::isSerialized] = false;
|
|
v[jss::isSigningField] = false;
|
|
v[jss::type] = "Amount";
|
|
a[1U] = v;
|
|
ret[jss::FIELDS][i++] = a;
|
|
}
|
|
|
|
for (auto const& [code, f] : ripple::SField::knownCodeToField)
|
|
{
|
|
if (f->fieldName == "")
|
|
continue;
|
|
|
|
Json::Value innerObj = Json::objectValue;
|
|
|
|
uint32_t fc = code & 0xFFU;
|
|
uint32_t tc = code >> 16U;
|
|
|
|
innerObj[jss::nth] = fc;
|
|
|
|
innerObj[jss::isVLEncoded] =
|
|
(tc == 7U /* Blob */ || tc == 8U /* AccountID */ ||
|
|
tc == 19U /* Vector256 */);
|
|
|
|
innerObj[jss::isSerialized] =
|
|
(tc <
|
|
10000); /* TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA */
|
|
|
|
innerObj[jss::isSigningField] = f->shouldInclude(false);
|
|
|
|
innerObj[jss::type] = type_map[tc];
|
|
|
|
Json::Value innerArray = Json::arrayValue;
|
|
innerArray[0U] = f->fieldName;
|
|
innerArray[1U] = innerObj;
|
|
|
|
ret[jss::FIELDS][i++] = innerArray;
|
|
}
|
|
|
|
ret[jss::TRANSACTION_RESULTS] = Json::objectValue;
|
|
for (auto const& entry : magic_enum::enum_entries<TELcodes>())
|
|
{
|
|
const auto name = entry.second;
|
|
ret[jss::TRANSACTION_RESULTS][STR(name)] =
|
|
static_cast<int32_t>(entry.first);
|
|
}
|
|
for (auto const& entry : magic_enum::enum_entries<TEMcodes>())
|
|
{
|
|
const auto name = entry.second;
|
|
ret[jss::TRANSACTION_RESULTS][STR(name)] =
|
|
static_cast<int32_t>(entry.first);
|
|
}
|
|
for (auto const& entry : magic_enum::enum_entries<TEFcodes>())
|
|
{
|
|
const auto name = entry.second;
|
|
ret[jss::TRANSACTION_RESULTS][STR(name)] =
|
|
static_cast<int32_t>(entry.first);
|
|
}
|
|
for (auto const& entry : magic_enum::enum_entries<TERcodes>())
|
|
{
|
|
const auto name = entry.second;
|
|
ret[jss::TRANSACTION_RESULTS][STR(name)] =
|
|
static_cast<int32_t>(entry.first);
|
|
}
|
|
for (auto const& entry : magic_enum::enum_entries<TEScodes>())
|
|
{
|
|
const auto name = entry.second;
|
|
ret[jss::TRANSACTION_RESULTS][STR(name)] =
|
|
static_cast<int32_t>(entry.first);
|
|
}
|
|
for (auto const& entry : magic_enum::enum_entries<TECcodes>())
|
|
{
|
|
const auto name = entry.second;
|
|
ret[jss::TRANSACTION_RESULTS][STR(name)] =
|
|
static_cast<int32_t>(entry.first);
|
|
}
|
|
|
|
auto const translate_tt = [](std::string inp) -> std::string {
|
|
if (inp == "Amendment")
|
|
return "EnableAmendment";
|
|
if (inp == "Fee")
|
|
return "SetFee";
|
|
if (inp == "PaychanClaim")
|
|
return "PaymentChannelClaim";
|
|
if (inp == "PaychanCreate")
|
|
return "PaymentChannelCreate";
|
|
if (inp == "PaychanFund")
|
|
return "PaymentChannelFund";
|
|
if (inp == "RegularKeySet")
|
|
return "SetRegularKey";
|
|
if (inp == "HookSet")
|
|
return "SetHook";
|
|
if (inp == "RemarksSet")
|
|
return "SetRemarks";
|
|
return inp;
|
|
};
|
|
|
|
ret[jss::TRANSACTION_TYPES] = Json::objectValue;
|
|
ret[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
|
|
for (auto const& entry : magic_enum::enum_entries<TxType>())
|
|
{
|
|
const auto name = entry.second;
|
|
std::string type_name = translate_tt(translate(name.data() + 2));
|
|
int32_t type_value = static_cast<int32_t>(entry.first);
|
|
ret[jss::TRANSACTION_TYPES][type_name] = type_value;
|
|
}
|
|
|
|
// Transaction Flags:
|
|
ret[jss::TRANSACTION_FLAGS] = Json::objectValue;
|
|
addFlagsToJson<UniversalFlags>(ret, "Universal");
|
|
addFlagsToJson<AccountSetFlags>(ret, "AccountSet");
|
|
addFlagsToJson<OfferCreateFlags>(ret, "OfferCreate");
|
|
addFlagsToJson<PaymentFlags>(ret, "Payment");
|
|
addFlagsToJson<TrustSetFlags>(ret, "TrustSet");
|
|
addFlagsToJson<EnableAmendmentFlags>(ret, "EnableAmendment");
|
|
addFlagsToJson<PaymentChannelClaimFlags>(ret, "PaymentChannelClaim");
|
|
addFlagsToJson<NFTokenMintFlags>(ret, "NFTokenMint");
|
|
addFlagsToJson<NFTokenCreateOfferFlags>(ret, "NFTokenCreateOffer");
|
|
addFlagsToJson<ClaimRewardFlags>(ret, "ClaimReward");
|
|
addFlagsToJson<BridgeModifyFlags>(ret, "XChainModifyBridge");
|
|
addFlagsToJson<MPTokenIssuanceCreateFlags>(ret, "MPTokenIssuanceCreate");
|
|
addFlagsToJson<MPTokenAuthorizeFlags>(ret, "MPTokenAuthorize");
|
|
addFlagsToJson<MPTokenIssuanceSetFlags>(ret, "MPTokenIssuanceSet");
|
|
addFlagsToJson<AMMClawbackFlags>(ret, "AMMClawback");
|
|
struct FlagData
|
|
{
|
|
std::string name;
|
|
std::uint32_t value;
|
|
};
|
|
// URITokenMint
|
|
std::array<FlagData, 1> uriTokenMintFlags{{{"tfBurnable", tfBurnable}}};
|
|
for (auto const& entry : uriTokenMintFlags)
|
|
{
|
|
ret[jss::TRANSACTION_FLAGS]["URITokenMint"][entry.name] =
|
|
static_cast<uint32_t>(entry.value);
|
|
}
|
|
// AMMWithdraw
|
|
std::array<FlagData, 7> ammWithdrawFlags{{
|
|
{"tfLPToken", tfLPToken},
|
|
{"tfSingleAsset", tfSingleAsset},
|
|
{"tfTwoAsset", tfTwoAsset},
|
|
{"tfOneAssetLPToken", tfOneAssetLPToken},
|
|
{"tfLimitLPToken", tfLimitLPToken},
|
|
{"tfWithdrawAll", tfWithdrawAll},
|
|
{"tfOneAssetWithdrawAll", tfOneAssetWithdrawAll}}};
|
|
for (auto const& entry : ammWithdrawFlags)
|
|
{
|
|
ret[jss::TRANSACTION_FLAGS]["AMMWithdraw"][entry.name] =
|
|
static_cast<uint32_t>(entry.value);
|
|
}
|
|
// AMM Deposit
|
|
std::array<FlagData, 6> ammDepositFlags{{
|
|
{"tfLPToken", tfLPToken},
|
|
{"tfSingleAsset", tfSingleAsset},
|
|
{"tfTwoAsset", tfTwoAsset},
|
|
{"tfOneAssetLPToken", tfOneAssetLPToken},
|
|
{"tfLimitLPToken", tfLimitLPToken},
|
|
{"tfTwoAssetIfEmpty", tfTwoAssetIfEmpty}}};
|
|
for (auto const& entry : ammDepositFlags)
|
|
{
|
|
ret[jss::TRANSACTION_FLAGS]["AMMDeposit"][entry.name] =
|
|
static_cast<uint32_t>(entry.value);
|
|
}
|
|
|
|
// Transaction Indicies Flags:
|
|
ret[jss::TRANSACTION_FLAGS_INDICES] = Json::objectValue;
|
|
for (auto const& entry : magic_enum::enum_entries<AccountFlags>())
|
|
{
|
|
const auto name = entry.second;
|
|
ret[jss::TRANSACTION_FLAGS_INDICES]["AccountSet"][STR(name)] =
|
|
static_cast<uint32_t>(entry.first);
|
|
}
|
|
|
|
ret[jss::native_currency_code] = systemCurrencyCode();
|
|
|
|
// generate hash
|
|
{
|
|
const std::string out = Json::FastWriter().write(ret);
|
|
defsHash =
|
|
ripple::sha512Half(ripple::Slice{out.data(), out.size()});
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::optional<uint256> defsHash;
|
|
Json::Value defs;
|
|
|
|
public:
|
|
Definitions() : defs(generate()) {};
|
|
|
|
uint256 const&
|
|
getHash() const
|
|
{
|
|
if (!defsHash)
|
|
{
|
|
static const uint256 fallbackHash(
|
|
"DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0");
|
|
return fallbackHash;
|
|
}
|
|
return *defsHash;
|
|
}
|
|
|
|
Json::Value const&
|
|
operator()(void) const
|
|
{
|
|
return defs;
|
|
}
|
|
};
|
|
|
|
Json::Value
|
|
doServerDefinitions(RPC::JsonContext& context)
|
|
{
|
|
auto& params = context.params;
|
|
|
|
uint256 reqHash;
|
|
if (params.isMember(jss::hash))
|
|
{
|
|
if (!params[jss::hash].isString() ||
|
|
!reqHash.parseHex(params[jss::hash].asString()))
|
|
return RPC::invalid_field_error(jss::hash);
|
|
}
|
|
|
|
uint32_t curLgrSeq = context.ledgerMaster.getValidatedLedger()->info().seq;
|
|
|
|
// static values used for cache
|
|
static thread_local uint32_t lastGenerated =
|
|
0; // last ledger seq it was generated
|
|
static thread_local Json::Value lastFeatures{
|
|
Json::objectValue}; // the actual features JSON last generated
|
|
static thread_local uint256
|
|
lastFeatureHash; // the hash of the features JSON last time
|
|
// it was generated
|
|
|
|
// if a flag ledger has passed since it was last generated, regenerate it,
|
|
// update the cache above
|
|
if (curLgrSeq > ((lastGenerated >> 8) + 1) << 8 || lastGenerated == 0)
|
|
{
|
|
majorityAmendments_t majorities;
|
|
if (auto const valLedger = context.ledgerMaster.getValidatedLedger())
|
|
majorities = getMajorityAmendments(*valLedger);
|
|
auto& table = context.app.getAmendmentTable();
|
|
bool const isAdmin = true;
|
|
auto features = table.getJson(isAdmin);
|
|
for (auto const& [h, t] : majorities)
|
|
features[to_string(h)][jss::majority] =
|
|
t.time_since_epoch().count();
|
|
|
|
lastFeatures = features;
|
|
{
|
|
const std::string out = Json::FastWriter().write(features);
|
|
lastFeatureHash =
|
|
ripple::sha512Half(ripple::Slice{out.data(), out.size()});
|
|
}
|
|
}
|
|
|
|
static const Definitions defs{};
|
|
|
|
// the hash is the xor of the two parts
|
|
uint256 retHash = lastFeatureHash ^ defs.getHash();
|
|
|
|
if (reqHash == retHash)
|
|
{
|
|
Json::Value jv = Json::objectValue;
|
|
jv[jss::hash] = to_string(retHash);
|
|
return jv;
|
|
}
|
|
|
|
// definitions
|
|
Json::Value ret = defs();
|
|
ret[jss::hash] = to_string(retHash);
|
|
ret[jss::features] = lastFeatures;
|
|
|
|
return ret;
|
|
}
|
|
|
|
} // namespace ripple
|