refactor: move server_definitions code to its own files (#5890)

This commit is contained in:
Mayukha Vadari
2025-10-20 18:24:48 -04:00
committed by GitHub
parent dd722f8b3f
commit ae719b86d3
4 changed files with 488 additions and 418 deletions

View File

@@ -0,0 +1,168 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 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 <test/jtx.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
namespace test {
class ServerDefinitions_test : public beast::unit_test::suite
{
public:
void
testServerDefinitions()
{
testcase("server_definitions");
using namespace test::jtx;
{
Env env(*this);
auto const result = env.rpc("server_definitions");
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
BEAST_EXPECT(result[jss::result][jss::status] == "success");
BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS));
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
// test a random element of each result
// (testing the whole output would be difficult to maintain)
{
auto const firstField = result[jss::result][jss::FIELDS][0u];
BEAST_EXPECT(firstField[0u].asString() == "Generic");
BEAST_EXPECT(
firstField[1][jss::isSerialized].asBool() == false);
BEAST_EXPECT(
firstField[1][jss::isSigningField].asBool() == false);
BEAST_EXPECT(firstField[1][jss::isVLEncoded].asBool() == false);
BEAST_EXPECT(firstField[1][jss::nth].asUInt() == 0);
BEAST_EXPECT(firstField[1][jss::type].asString() == "Unknown");
}
BEAST_EXPECT(
result[jss::result][jss::LEDGER_ENTRY_TYPES]["AccountRoot"]
.asUInt() == 97);
BEAST_EXPECT(
result[jss::result][jss::TRANSACTION_RESULTS]["tecDIR_FULL"]
.asUInt() == 121);
BEAST_EXPECT(
result[jss::result][jss::TRANSACTION_TYPES]["Payment"]
.asUInt() == 0);
BEAST_EXPECT(
result[jss::result][jss::TYPES]["AccountID"].asUInt() == 8);
// check exception SFields
{
auto const fieldExists = [&](std::string name) {
for (auto& field : result[jss::result][jss::FIELDS])
{
if (field[0u].asString() == name)
{
return true;
}
}
return false;
};
BEAST_EXPECT(fieldExists("Generic"));
BEAST_EXPECT(fieldExists("Invalid"));
BEAST_EXPECT(fieldExists("ObjectEndMarker"));
BEAST_EXPECT(fieldExists("ArrayEndMarker"));
BEAST_EXPECT(fieldExists("taker_gets_funded"));
BEAST_EXPECT(fieldExists("taker_pays_funded"));
BEAST_EXPECT(fieldExists("hash"));
BEAST_EXPECT(fieldExists("index"));
}
// test that base_uint types are replaced with "Hash" prefix
{
auto const types = result[jss::result][jss::TYPES];
BEAST_EXPECT(types["Hash128"].asUInt() == 4);
BEAST_EXPECT(types["Hash160"].asUInt() == 17);
BEAST_EXPECT(types["Hash192"].asUInt() == 21);
BEAST_EXPECT(types["Hash256"].asUInt() == 5);
BEAST_EXPECT(types["Hash384"].asUInt() == 22);
BEAST_EXPECT(types["Hash512"].asUInt() == 23);
}
}
// test providing the same hash
{
Env env(*this);
auto const firstResult = env.rpc("server_definitions");
auto const hash = firstResult[jss::result][jss::hash].asString();
auto const hashParam =
std::string("{ ") + "\"hash\": \"" + hash + "\"}";
auto const result =
env.rpc("json", "server_definitions", hashParam);
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
BEAST_EXPECT(result[jss::result][jss::status] == "success");
BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS));
BEAST_EXPECT(
!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
BEAST_EXPECT(
!result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
}
// test providing a different hash
{
Env env(*this);
std::string const hash =
"54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749"
"D1";
auto const hashParam =
std::string("{ ") + "\"hash\": \"" + hash + "\"}";
auto const result =
env.rpc("json", "server_definitions", hashParam);
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
BEAST_EXPECT(result[jss::result][jss::status] == "success");
BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS));
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
}
}
void
run() override
{
testServerDefinitions();
}
};
BEAST_DEFINE_TESTSUITE(ServerDefinitions, rpc, ripple);
} // namespace test
} // namespace ripple

View File

@@ -174,137 +174,10 @@ admin = 127.0.0.1
}
}
void
testServerDefinitions()
{
testcase("server_definitions");
using namespace test::jtx;
{
Env env(*this);
auto const result = env.rpc("server_definitions");
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
BEAST_EXPECT(result[jss::result][jss::status] == "success");
BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS));
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
// test a random element of each result
// (testing the whole output would be difficult to maintain)
{
auto const firstField = result[jss::result][jss::FIELDS][0u];
BEAST_EXPECT(firstField[0u].asString() == "Generic");
BEAST_EXPECT(
firstField[1][jss::isSerialized].asBool() == false);
BEAST_EXPECT(
firstField[1][jss::isSigningField].asBool() == false);
BEAST_EXPECT(firstField[1][jss::isVLEncoded].asBool() == false);
BEAST_EXPECT(firstField[1][jss::nth].asUInt() == 0);
BEAST_EXPECT(firstField[1][jss::type].asString() == "Unknown");
}
BEAST_EXPECT(
result[jss::result][jss::LEDGER_ENTRY_TYPES]["AccountRoot"]
.asUInt() == 97);
BEAST_EXPECT(
result[jss::result][jss::TRANSACTION_RESULTS]["tecDIR_FULL"]
.asUInt() == 121);
BEAST_EXPECT(
result[jss::result][jss::TRANSACTION_TYPES]["Payment"]
.asUInt() == 0);
BEAST_EXPECT(
result[jss::result][jss::TYPES]["AccountID"].asUInt() == 8);
// check exception SFields
{
auto const fieldExists = [&](std::string name) {
for (auto& field : result[jss::result][jss::FIELDS])
{
if (field[0u].asString() == name)
{
return true;
}
}
return false;
};
BEAST_EXPECT(fieldExists("Generic"));
BEAST_EXPECT(fieldExists("Invalid"));
BEAST_EXPECT(fieldExists("ObjectEndMarker"));
BEAST_EXPECT(fieldExists("ArrayEndMarker"));
BEAST_EXPECT(fieldExists("taker_gets_funded"));
BEAST_EXPECT(fieldExists("taker_pays_funded"));
BEAST_EXPECT(fieldExists("hash"));
BEAST_EXPECT(fieldExists("index"));
}
// test that base_uint types are replaced with "Hash" prefix
{
auto const types = result[jss::result][jss::TYPES];
BEAST_EXPECT(types["Hash128"].asUInt() == 4);
BEAST_EXPECT(types["Hash160"].asUInt() == 17);
BEAST_EXPECT(types["Hash192"].asUInt() == 21);
BEAST_EXPECT(types["Hash256"].asUInt() == 5);
BEAST_EXPECT(types["Hash384"].asUInt() == 22);
BEAST_EXPECT(types["Hash512"].asUInt() == 23);
}
}
// test providing the same hash
{
Env env(*this);
auto const firstResult = env.rpc("server_definitions");
auto const hash = firstResult[jss::result][jss::hash].asString();
auto const hashParam =
std::string("{ ") + "\"hash\": \"" + hash + "\"}";
auto const result =
env.rpc("json", "server_definitions", hashParam);
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
BEAST_EXPECT(result[jss::result][jss::status] == "success");
BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS));
BEAST_EXPECT(
!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
BEAST_EXPECT(
!result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
}
// test providing a different hash
{
Env env(*this);
std::string const hash =
"54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749"
"D1";
auto const hashParam =
std::string("{ ") + "\"hash\": \"" + hash + "\"}";
auto const result =
env.rpc("json", "server_definitions", hashParam);
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
BEAST_EXPECT(result[jss::result][jss::status] == "success");
BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS));
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
}
}
void
run() override
{
testServerInfo();
testServerDefinitions();
}
};

View File

@@ -0,0 +1,320 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 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/Context.h>
#include <xrpld/rpc/Role.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <boost/algorithm/string.hpp>
#include <unordered_map>
namespace ripple {
namespace detail {
class ServerDefinitions
{
private:
std::string
// translate e.g. STI_LEDGERENTRY to LedgerEntry
translate(std::string const& inp);
uint256 defsHash_;
Json::Value defs_;
public:
ServerDefinitions();
bool
hashMatches(uint256 hash) const
{
return defsHash_ == hash;
}
Json::Value const&
get() const
{
return defs_;
}
};
std::string
ServerDefinitions::translate(std::string const& inp)
{
auto replace = [&](char const* oldStr, char const* newStr) -> std::string {
std::string out = inp;
boost::replace_all(out, oldStr, newStr);
return out;
};
auto contains = [&](char const* s) -> bool {
return inp.find(s) != std::string::npos;
};
if (contains("UINT"))
{
if (contains("512") || contains("384") || contains("256") ||
contains("192") || contains("160") || contains("128"))
return replace("UINT", "Hash");
else
return replace("UINT", "UInt");
}
std::unordered_map<std::string, std::string> replacements{
{"OBJECT", "STObject"},
{"ARRAY", "STArray"},
{"ACCOUNT", "AccountID"},
{"LEDGERENTRY", "LedgerEntry"},
{"NOTPRESENT", "NotPresent"},
{"PATHSET", "PathSet"},
{"VL", "Blob"},
{"XCHAIN_BRIDGE", "XChainBridge"},
};
if (auto const& it = replacements.find(inp); it != replacements.end())
{
return it->second;
}
std::string out;
size_t pos = 0;
std::string inpToProcess = inp;
// convert snake_case to CamelCase
for (;;)
{
pos = inpToProcess.find("_");
if (pos == std::string::npos)
pos = inpToProcess.size();
std::string token = inpToProcess.substr(0, pos);
if (token.size() > 1)
{
boost::algorithm::to_lower(token);
token.data()[0] -= ('a' - 'A');
out += token;
}
else
out += token;
if (pos == inpToProcess.size())
break;
inpToProcess = inpToProcess.substr(pos + 1);
}
return out;
};
ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue}
{
// populate SerializedTypeID names and values
defs_[jss::TYPES] = Json::objectValue;
defs_[jss::TYPES]["Done"] = -1;
std::map<int32_t, std::string> typeMap{{-1, "Done"}};
for (auto const& [rawName, typeValue] : sTypeMap)
{
std::string typeName =
translate(std::string(rawName).substr(4) /* remove STI_ */);
defs_[jss::TYPES][typeName] = typeValue;
typeMap[typeValue] = typeName;
}
// populate LedgerEntryType names and values
defs_[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
defs_[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
for (auto const& f : LedgerFormats::getInstance())
{
defs_[jss::LEDGER_ENTRY_TYPES][f.getName()] = f.getType();
}
// populate SField serialization data
defs_[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;
defs_[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;
defs_[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] = true;
v[jss::isSigningField] = true;
v[jss::type] = "STObject";
a[1U] = v;
defs_[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] = true;
v[jss::isSigningField] = true;
v[jss::type] = "STArray";
a[1U] = v;
defs_[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;
defs_[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;
defs_[jss::FIELDS][i++] = a;
}
for (auto const& [code, f] : ripple::SField::getKnownCodeToField())
{
if (f->fieldName == "")
continue;
Json::Value innerObj = Json::objectValue;
uint32_t type = f->fieldType;
innerObj[jss::nth] = f->fieldValue;
// whether the field is variable-length encoded
// this means that the length is included before the content
innerObj[jss::isVLEncoded] =
(type == 7U /* Blob */ || type == 8U /* AccountID */ ||
type == 19U /* Vector256 */);
// whether the field is included in serialization
innerObj[jss::isSerialized] =
(type < 10000 && f->fieldName != "hash" &&
f->fieldName != "index"); /* hash, index, TRANSACTION,
LEDGER_ENTRY, VALIDATION, METADATA */
// whether the field is included in serialization when signing
innerObj[jss::isSigningField] = f->shouldInclude(false);
innerObj[jss::type] = typeMap[type];
Json::Value innerArray = Json::arrayValue;
innerArray[0U] = f->fieldName;
innerArray[1U] = innerObj;
defs_[jss::FIELDS][i++] = innerArray;
}
// populate TER code names and values
defs_[jss::TRANSACTION_RESULTS] = Json::objectValue;
for (auto const& [code, terInfo] : transResults())
{
defs_[jss::TRANSACTION_RESULTS][terInfo.first] = code;
}
// populate TxType names and values
defs_[jss::TRANSACTION_TYPES] = Json::objectValue;
defs_[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
for (auto const& f : TxFormats::getInstance())
{
defs_[jss::TRANSACTION_TYPES][f.getName()] = f.getType();
}
// generate hash
{
std::string const out = Json::FastWriter().write(defs_);
defsHash_ = ripple::sha512Half(ripple::Slice{out.data(), out.size()});
defs_[jss::hash] = to_string(defsHash_);
}
}
} // namespace detail
Json::Value
doServerDefinitions(RPC::JsonContext& context)
{
auto& params = context.params;
uint256 hash;
if (params.isMember(jss::hash))
{
if (!params[jss::hash].isString() ||
!hash.parseHex(params[jss::hash].asString()))
return RPC::invalid_field_error(jss::hash);
}
static detail::ServerDefinitions const defs{};
if (defs.hashMatches(hash))
{
Json::Value jv = Json::objectValue;
jv[jss::hash] = to_string(hash);
return jv;
}
return defs.get();
}
} // namespace ripple

View File

@@ -23,301 +23,10 @@
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <boost/algorithm/string.hpp>
#include <unordered_map>
namespace ripple {
namespace detail {
class ServerDefinitions
{
private:
std::string
// translate e.g. STI_LEDGERENTRY to LedgerEntry
translate(std::string const& inp);
uint256 defsHash_;
Json::Value defs_;
public:
ServerDefinitions();
bool
hashMatches(uint256 hash) const
{
return defsHash_ == hash;
}
Json::Value const&
get() const
{
return defs_;
}
};
std::string
ServerDefinitions::translate(std::string const& inp)
{
auto replace = [&](char const* oldStr, char const* newStr) -> std::string {
std::string out = inp;
boost::replace_all(out, oldStr, newStr);
return out;
};
auto contains = [&](char const* s) -> bool {
return inp.find(s) != std::string::npos;
};
if (contains("UINT"))
{
if (contains("512") || contains("384") || contains("256") ||
contains("192") || contains("160") || contains("128"))
return replace("UINT", "Hash");
else
return replace("UINT", "UInt");
}
std::unordered_map<std::string, std::string> replacements{
{"OBJECT", "STObject"},
{"ARRAY", "STArray"},
{"ACCOUNT", "AccountID"},
{"LEDGERENTRY", "LedgerEntry"},
{"NOTPRESENT", "NotPresent"},
{"PATHSET", "PathSet"},
{"VL", "Blob"},
{"XCHAIN_BRIDGE", "XChainBridge"},
};
if (auto const& it = replacements.find(inp); it != replacements.end())
{
return it->second;
}
std::string out;
size_t pos = 0;
std::string inpToProcess = inp;
// convert snake_case to CamelCase
for (;;)
{
pos = inpToProcess.find("_");
if (pos == std::string::npos)
pos = inpToProcess.size();
std::string token = inpToProcess.substr(0, pos);
if (token.size() > 1)
{
boost::algorithm::to_lower(token);
token.data()[0] -= ('a' - 'A');
out += token;
}
else
out += token;
if (pos == inpToProcess.size())
break;
inpToProcess = inpToProcess.substr(pos + 1);
}
return out;
};
ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue}
{
// populate SerializedTypeID names and values
defs_[jss::TYPES] = Json::objectValue;
defs_[jss::TYPES]["Done"] = -1;
std::map<int32_t, std::string> typeMap{{-1, "Done"}};
for (auto const& [rawName, typeValue] : sTypeMap)
{
std::string typeName =
translate(std::string(rawName).substr(4) /* remove STI_ */);
defs_[jss::TYPES][typeName] = typeValue;
typeMap[typeValue] = typeName;
}
// populate LedgerEntryType names and values
defs_[jss::LEDGER_ENTRY_TYPES] = Json::objectValue;
defs_[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
for (auto const& f : LedgerFormats::getInstance())
{
defs_[jss::LEDGER_ENTRY_TYPES][f.getName()] = f.getType();
}
// populate SField serialization data
defs_[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;
defs_[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;
defs_[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] = true;
v[jss::isSigningField] = true;
v[jss::type] = "STObject";
a[1U] = v;
defs_[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] = true;
v[jss::isSigningField] = true;
v[jss::type] = "STArray";
a[1U] = v;
defs_[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;
defs_[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;
defs_[jss::FIELDS][i++] = a;
}
for (auto const& [code, f] : ripple::SField::getKnownCodeToField())
{
if (f->fieldName == "")
continue;
Json::Value innerObj = Json::objectValue;
uint32_t type = f->fieldType;
innerObj[jss::nth] = f->fieldValue;
// whether the field is variable-length encoded
// this means that the length is included before the content
innerObj[jss::isVLEncoded] =
(type == 7U /* Blob */ || type == 8U /* AccountID */ ||
type == 19U /* Vector256 */);
// whether the field is included in serialization
innerObj[jss::isSerialized] =
(type < 10000 && f->fieldName != "hash" &&
f->fieldName != "index"); /* hash, index, TRANSACTION,
LEDGER_ENTRY, VALIDATION, METADATA */
// whether the field is included in serialization when signing
innerObj[jss::isSigningField] = f->shouldInclude(false);
innerObj[jss::type] = typeMap[type];
Json::Value innerArray = Json::arrayValue;
innerArray[0U] = f->fieldName;
innerArray[1U] = innerObj;
defs_[jss::FIELDS][i++] = innerArray;
}
// populate TER code names and values
defs_[jss::TRANSACTION_RESULTS] = Json::objectValue;
for (auto const& [code, terInfo] : transResults())
{
defs_[jss::TRANSACTION_RESULTS][terInfo.first] = code;
}
// populate TxType names and values
defs_[jss::TRANSACTION_TYPES] = Json::objectValue;
defs_[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
for (auto const& f : TxFormats::getInstance())
{
defs_[jss::TRANSACTION_TYPES][f.getName()] = f.getType();
}
// generate hash
{
std::string const out = Json::FastWriter().write(defs_);
defsHash_ = ripple::sha512Half(ripple::Slice{out.data(), out.size()});
defs_[jss::hash] = to_string(defsHash_);
}
}
} // namespace detail
Json::Value
doServerDefinitions(RPC::JsonContext& context)
{
auto& params = context.params;
uint256 hash;
if (params.isMember(jss::hash))
{
if (!params[jss::hash].isString() ||
!hash.parseHex(params[jss::hash].asString()))
return RPC::invalid_field_error(jss::hash);
}
static detail::ServerDefinitions const defs{};
if (defs.hashMatches(hash))
{
Json::Value jv = Json::objectValue;
jv[jss::hash] = to_string(hash);
return jv;
}
return defs.get();
}
Json::Value
doServerInfo(RPC::JsonContext& context)
{