mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 11:15:56 +00:00
refactor: move server_definitions code to its own files (#5890)
This commit is contained in:
168
src/test/rpc/ServerDefinitions_test.cpp
Normal file
168
src/test/rpc/ServerDefinitions_test.cpp
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
320
src/xrpld/rpc/handlers/ServerDefinitions.cpp
Normal file
320
src/xrpld/rpc/handlers/ServerDefinitions.cpp
Normal 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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user