xrpld
Loading...
Searching...
No Matches
ServerDefinitions.cpp
1#include <xrpld/rpc/handlers/server_info/ServerDefinitions.h>
2
3#include <xrpld/rpc/Context.h>
4
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/json/json_value.h>
7#include <xrpl/json/json_writer.h>
8#include <xrpl/protocol/ErrorCodes.h>
9#include <xrpl/protocol/LedgerFormats.h>
10#include <xrpl/protocol/SField.h>
11#include <xrpl/protocol/TER.h>
12#include <xrpl/protocol/TxFlags.h>
13#include <xrpl/protocol/TxFormats.h>
14#include <xrpl/protocol/digest.h>
15#include <xrpl/protocol/jss.h>
16
17#include <boost/algorithm/string/case_conv.hpp>
18#include <boost/algorithm/string/replace.hpp>
19
20#include <cstddef>
21#include <cstdint>
22#include <map>
23#include <set>
24#include <string>
25#include <string_view>
26#include <unordered_map>
27
28namespace xrpl {
29
30namespace detail {
31
33{
34private:
35 static std::string
36 // translate e.g. STI_LEDGERENTRY to LedgerEntry
37 translate(std::string const& inp);
38
41
42public:
44
45 [[nodiscard]] bool
46 hashMatches(uint256 hash) const
47 {
48 return defsHash_ == hash;
49 }
50
51 [[nodiscard]] json::Value const&
52 get() const
53 {
54 return defs_;
55 }
56};
57
60{
61 auto replace = [&](std::string_view oldStr, std::string_view newStr) -> std::string {
62 std::string out = inp;
63 boost::replace_all(out, oldStr, newStr);
64 return out;
65 };
66
67 // TODO: use string::contains with C++23
68 auto contains = [&](std::string_view s) -> bool { return inp.contains(s); };
69
70 if (contains("UINT"))
71 {
72 if (contains("512") || contains("384") || contains("256") || contains("192") ||
73 contains("160") || contains("128"))
74 {
75 return replace("UINT", "Hash");
76 }
77
78 return replace("UINT", "UInt");
79 }
80
82 {"OBJECT", "STObject"},
83 {"ARRAY", "STArray"},
84 {"ACCOUNT", "AccountID"},
85 {"LEDGERENTRY", "LedgerEntry"},
86 {"NOTPRESENT", "NotPresent"},
87 {"PATHSET", "PathSet"},
88 {"VL", "Blob"},
89 {"XCHAIN_BRIDGE", "XChainBridge"},
90 };
91
92 if (auto const& it = kReplacements.find(inp); it != kReplacements.end())
93 {
94 return std::string(it->second);
95 }
96
97 std::string out;
98 size_t pos = 0;
99 std::string inpToProcess = inp;
100
101 // convert snake_case to CamelCase
102 for (;;)
103 {
104 pos = inpToProcess.find('_');
105 if (pos == std::string::npos)
106 pos = inpToProcess.size();
107 std::string token = inpToProcess.substr(0, pos);
108 if (token.size() > 1)
109 {
110 boost::algorithm::to_lower(token);
111 token.data()[0] -= ('a' - 'A');
112 out += token;
113 }
114 else
115 {
116 out += token;
117 }
118 if (pos == inpToProcess.size())
119 break;
120 inpToProcess = inpToProcess.substr(pos + 1);
121 }
122 return out;
123};
124
126{
127 // populate SerializedTypeID names and values
128 defs_[jss::TYPES] = json::ValueType::Object;
129
130 defs_[jss::TYPES]["Done"] = -1;
131 std::map<int32_t, std::string> typeMap{{-1, "Done"}};
132 for (auto const& [rawName, typeValue] : kSTypeMap)
133 {
134 std::string const typeName = translate(std::string(rawName).substr(4) /* remove STI_ */);
135 defs_[jss::TYPES][typeName] = typeValue;
136 typeMap[typeValue] = typeName;
137 }
138
139 // populate LedgerEntryType names and values
140 defs_[jss::LEDGER_ENTRY_TYPES] = json::ValueType::Object;
141 defs_[jss::LEDGER_ENTRY_TYPES][jss::Invalid] = -1;
142
143 for (auto const& f : LedgerFormats::getInstance())
144 {
145 defs_[jss::LEDGER_ENTRY_TYPES][f.getName()] = f.getType();
146 }
147
148 // populate SField serialization data
149 defs_[jss::FIELDS] = json::ValueType::Array;
150
151 uint32_t i = 0;
152
153 {
155 a[0U] = "Invalid";
157 v[jss::nth] = -1;
158 v[jss::isVLEncoded] = false;
159 v[jss::isSerialized] = false;
160 v[jss::isSigningField] = false;
161 v[jss::type] = "Unknown";
162 a[1U] = v;
163 defs_[jss::FIELDS][i++] = a;
164 }
165
166 {
168 a[0U] = "ObjectEndMarker";
170 v[jss::nth] = 1;
171 v[jss::isVLEncoded] = false;
172 v[jss::isSerialized] = true;
173 v[jss::isSigningField] = true;
174 v[jss::type] = "STObject";
175 a[1U] = v;
176 defs_[jss::FIELDS][i++] = a;
177 }
178
179 {
181 a[0U] = "ArrayEndMarker";
183 v[jss::nth] = 1;
184 v[jss::isVLEncoded] = false;
185 v[jss::isSerialized] = true;
186 v[jss::isSigningField] = true;
187 v[jss::type] = "STArray";
188 a[1U] = v;
189 defs_[jss::FIELDS][i++] = a;
190 }
191
192 {
194 a[0U] = "taker_gets_funded";
196 v[jss::nth] = 258;
197 v[jss::isVLEncoded] = false;
198 v[jss::isSerialized] = false;
199 v[jss::isSigningField] = false;
200 v[jss::type] = "Amount";
201 a[1U] = v;
202 defs_[jss::FIELDS][i++] = a;
203 }
204
205 {
207 a[0U] = "taker_pays_funded";
209 v[jss::nth] = 259;
210 v[jss::isVLEncoded] = false;
211 v[jss::isSerialized] = false;
212 v[jss::isSigningField] = false;
213 v[jss::type] = "Amount";
214 a[1U] = v;
215 defs_[jss::FIELDS][i++] = a;
216 }
217
218 // copy into a sorted map to ensure deterministic output order (sorted by fieldCode)
219 static std::map<int, SField const*> const kSortedFields(
221
222 for (auto const& [code, field] : kSortedFields)
223 {
224 if (field->fieldName.empty())
225 continue;
226
228
229 int32_t const type = field->fieldType;
230
231 innerObj[jss::nth] = field->fieldValue;
232
233 // whether the field is variable-length encoded this means that the length is included
234 // before the content
235 innerObj[jss::isVLEncoded] =
236 (type == STI_VL || type == STI_ACCOUNT || type == STI_VECTOR256);
237 static_assert(
238 STI_VL == 7U && STI_ACCOUNT == 8U && STI_VECTOR256 == 19U,
239 "STI_VL, STI_ACCOUNT, STI_VECTOR256 must be 7, 8, 19 respectively");
240
241 // whether the field is included in serialization
242 innerObj[jss::isSerialized] =
243 (type < 10000 && field->fieldName != "hash" &&
244 field->fieldName !=
245 "index"); // hash, index, TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA
246
247 // whether the field is included in serialization when signing
248 innerObj[jss::isSigningField] = field->shouldInclude(false);
249
250 innerObj[jss::type] = typeMap[type];
251
253 innerArray[0U] = field->fieldName;
254 innerArray[1U] = innerObj;
255
256 defs_[jss::FIELDS][i++] = innerArray;
257 }
258
259 // populate TER code names and values
260 defs_[jss::TRANSACTION_RESULTS] = json::ValueType::Object;
261
262 for (auto const& [code, terInfo] : transResults())
263 {
264 defs_[jss::TRANSACTION_RESULTS][terInfo.first] = code;
265 }
266
267 // populate TxType names and values
268 defs_[jss::TRANSACTION_TYPES] = json::ValueType::Object;
269 defs_[jss::TRANSACTION_TYPES][jss::Invalid] = -1;
270 for (auto const& f : TxFormats::getInstance())
271 {
272 defs_[jss::TRANSACTION_TYPES][f.getName()] = f.getType();
273 }
274
275 // populate TxFormats
276 defs_[jss::TRANSACTION_FORMATS] = json::ValueType::Object;
277
278 defs_[jss::TRANSACTION_FORMATS][jss::common] = json::ValueType::Array;
279 auto txCommonFields = std::set<std::string>();
280 for (auto const& element : TxFormats::getCommonFields())
281 {
283 elementObj[jss::name] = element.sField().getName();
284 elementObj[jss::optionality] = element.style();
285 defs_[jss::TRANSACTION_FORMATS][jss::common].append(elementObj);
286 txCommonFields.insert(element.sField().getName());
287 }
288
289 for (auto const& format : TxFormats::getInstance())
290 {
291 auto const& soTemplate = format.getSOTemplate();
292 json::Value templateArray = json::ValueType::Array;
293 for (auto const& element : soTemplate)
294 {
295 if (txCommonFields.contains(element.sField().getName()))
296 continue; // skip common fields, already added
298 elementObj[jss::name] = element.sField().getName();
299 elementObj[jss::optionality] = element.style();
300 templateArray.append(elementObj);
301 }
302 defs_[jss::TRANSACTION_FORMATS][format.getName()] = templateArray;
303 }
304
305 // populate LedgerFormats
306 defs_[jss::LEDGER_ENTRY_FORMATS] = json::ValueType::Object;
307 defs_[jss::LEDGER_ENTRY_FORMATS][jss::common] = json::ValueType::Array;
308 auto ledgerCommonFields = std::set<std::string>();
309 for (auto const& element : LedgerFormats::getCommonFields())
310 {
312 elementObj[jss::name] = element.sField().getName();
313 elementObj[jss::optionality] = element.style();
314 defs_[jss::LEDGER_ENTRY_FORMATS][jss::common].append(elementObj);
315 ledgerCommonFields.insert(element.sField().getName());
316 }
317 for (auto const& format : LedgerFormats::getInstance())
318 {
319 auto const& soTemplate = format.getSOTemplate();
320 json::Value templateArray = json::ValueType::Array;
321 for (auto const& element : soTemplate)
322 {
323 if (ledgerCommonFields.contains(element.sField().getName()))
324 continue; // skip common fields, already added
326 elementObj[jss::name] = element.sField().getName();
327 elementObj[jss::optionality] = element.style();
328 templateArray.append(elementObj);
329 }
330 defs_[jss::LEDGER_ENTRY_FORMATS][format.getName()] = templateArray;
331 }
332
333 defs_[jss::TRANSACTION_FLAGS] = json::ValueType::Object;
334 for (auto const& [name, value] : getAllTxFlags())
335 {
337 for (auto const& [flagName, flagValue] : value)
338 {
339 txObj[flagName] = flagValue;
340 }
341 defs_[jss::TRANSACTION_FLAGS][name] = txObj;
342 }
343
344 defs_[jss::LEDGER_ENTRY_FLAGS] = json::ValueType::Object;
345 for (auto const& [name, value] : getAllLedgerFlags())
346 {
348 for (auto const& [flagName, flagValue] : value)
349 {
350 ledgerObj[flagName] = flagValue;
351 }
352 defs_[jss::LEDGER_ENTRY_FLAGS][name] = ledgerObj;
353 }
354
355 defs_[jss::ACCOUNT_SET_FLAGS] = json::ValueType::Object;
356 for (auto const& [name, value] : getAsfFlagMap())
357 {
358 defs_[jss::ACCOUNT_SET_FLAGS][name] = value;
359 }
360
361 // generate hash
362 {
365 defs_[jss::hash] = to_string(defsHash_);
366 }
367}
368
371{
372 static ServerDefinitions const kDefs{};
373 return kDefs;
374}
375
376} // namespace detail
377
378json::Value const&
383
386{
387 auto& params = context.params;
388
389 uint256 hash;
390 if (params.isMember(jss::hash))
391 {
392 if (!params[jss::hash].isString() || !hash.parseHex(params[jss::hash].asString()))
393 return RPC::invalidFieldError(jss::hash);
394 }
395
396 auto const& defs = detail::getDefinitions();
397 if (defs.hashMatches(hash))
398 {
400 jv[jss::hash] = to_string(hash);
401 return jv;
402 }
403 return defs.get();
404}
405
406} // namespace xrpl
Outputs a Value in JSON format without formatting (not human friendly).
Definition json_writer.h:32
std::string write(Value const &root) override
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:507
static std::vector< SOElement > const & getCommonFields()
static LedgerFormats const & getInstance()
static std::unordered_map< int, SField const * > const & getKnownCodeToField()
Definition SField.h:290
An immutable linear range of bytes.
Definition Slice.h:26
static std::vector< SOElement > const & getCommonFields()
Definition TxFormats.cpp:13
static TxFormats const & getInstance()
Definition TxFormats.cpp:57
json::Value const & get() const
static std::string translate(std::string const &inp)
bool hashMatches(uint256 hash) const
T data(T... args)
T end(T... args)
T find(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:5
@ Array
array value (ordered list)
Definition json_value.h:25
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
json::Value invalidFieldError(std::string const &name)
Definition ErrorCodes.h:273
ServerDefinitions const & getDefinitions()
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:204
json::Value const & getServerDefinitionsJson()
static std::map< std::string, int > const kSTypeMap
Definition SField.h:95
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
FlagMapPairList const & getAllTxFlags()
Definition TxFlags.h:306
std::unordered_map< TERUnderlyingType, std::pair< char const *const, char const *const > > const & transResults()
Definition TER.cpp:14
std::vector< std::pair< std::string, LedgerFlagMap > > const & getAllLedgerFlags()
std::map< std::string, FlagValue > const & getAsfFlagMap()
Definition TxFlags.h:432
json::Value doServerDefinitions(RPC::JsonContext &)
BaseUInt< 256 > uint256
Definition base_uint.h:562
T size(T... args)
json::Value params
Definition Context.h:43
T substr(T... args)