xrpld
Loading...
Searching...
No Matches
Feature_test.cpp
1
2#include <test/jtx/Env.h>
3#include <test/jtx/envconfig.h>
4
5#include <xrpld/core/Config.h>
6
7#include <xrpl/basics/base_uint.h>
8#include <xrpl/beast/unit_test/suite.h>
9#include <xrpl/config/Constants.h>
10#include <xrpl/json/json_value.h>
11#include <xrpl/json/to_string.h>
12#include <xrpl/ledger/AmendmentTable.h>
13#include <xrpl/ledger/View.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/digest.h>
16#include <xrpl/protocol/jss.h>
17
18#include <algorithm>
19#include <cstddef>
20#include <iterator>
21#include <map>
22#include <memory>
23
24namespace xrpl {
25
27{
28 void
30 {
31 testcase("internals");
32
33 auto const& supportedAmendments = xrpl::detail::supportedAmendments();
34 auto const& allAmendments = xrpl::allAmendments();
35
36 BEAST_EXPECT(
37 supportedAmendments.size() ==
39 {
40 std::size_t up = 0, down = 0, obsolete = 0;
41 for (auto const& [name, vote] : supportedAmendments)
42 {
43 switch (vote)
44 {
46 ++up;
47 break;
49 ++down;
50 break;
52 ++obsolete;
53 break;
54 default:
55 fail("Unknown VoteBehavior", __FILE__, __LINE__);
56 }
57
58 if (vote == VoteBehavior::Obsolete)
59 {
60 BEAST_EXPECT(
61 allAmendments.contains(name) &&
63 }
64 else
65 {
66 BEAST_EXPECT(
67 allAmendments.contains(name) &&
69 }
70 }
71 BEAST_EXPECT(down + obsolete == xrpl::detail::numDownVotedAmendments());
72 BEAST_EXPECT(up == xrpl::detail::numUpVotedAmendments());
73 }
74 {
75 std::size_t supported = 0, unsupported = 0, retired = 0;
76 for (auto const& [name, support] : allAmendments)
77 {
78 switch (support)
79 {
81 ++supported;
82 BEAST_EXPECT(supportedAmendments.contains(name));
83 break;
85 ++unsupported;
86 break;
88 ++retired;
89 break;
90 default:
91 fail("Unknown AmendmentSupport", __FILE__, __LINE__);
92 }
93 }
94
95 BEAST_EXPECT(supported + retired == supportedAmendments.size());
96 BEAST_EXPECT(allAmendments.size() - unsupported == supportedAmendments.size());
97 }
98 }
99
100 void
102 {
103 testcase("featureToName");
104
105 // Test all the supported features. In a perfect world, this would test
106 // FeatureCollections::featureNames, but that's private. Leave it that
107 // way.
108 auto const supported = xrpl::detail::supportedAmendments();
109
110 for (auto const& [feature, vote] : supported)
111 {
112 (void)vote;
113 auto const registered = getRegisteredFeature(feature);
114
115 if (BEAST_EXPECT(registered); registered.has_value())
116 {
117 BEAST_EXPECT(featureToName(*registered) == feature);
118 BEAST_EXPECT(
119 bitsetIndexToFeature(featureToBitsetIndex(*registered)) == *registered);
120 }
121 }
122
123 // Test an arbitrary unknown feature
124 uint256 const zero{0};
125 BEAST_EXPECT(featureToName(zero) == to_string(zero));
126 BEAST_EXPECT(
127 featureToName(zero) ==
128 "0000000000000000000000000000000000000000000000000000000000000000");
129
130 // Test looking up an unknown feature
131 BEAST_EXPECT(!getRegisteredFeature("unknown"));
132
133 // Test a random sampling of the variables. If any of these get retired
134 // or removed, swap out for any other feature.
135 BEAST_EXPECT(
136 featureToName(fixRemoveNFTokenAutoTrustLine) == "fixRemoveNFTokenAutoTrustLine");
137 BEAST_EXPECT(featureToName(featureBatch) == "Batch");
138 BEAST_EXPECT(featureToName(featureDID) == "DID");
139 BEAST_EXPECT(featureToName(fixIncludeKeyletFields) == "fixIncludeKeyletFields");
140 BEAST_EXPECT(featureToName(featureTokenEscrow) == "TokenEscrow");
141 }
142
143 void
145 {
146 testcase("No Params, None Enabled");
147
148 using namespace test::jtx;
149 Env env{*this};
150
152
153 auto jrr = env.rpc("feature")[jss::result];
154 if (!BEAST_EXPECT(jrr.isMember(jss::features)))
155 return;
156 for (auto const& feature : jrr[jss::features])
157 {
158 if (!BEAST_EXPECT(feature.isMember(jss::name)))
159 return;
160 // default config - so all should be disabled, and
161 // supported. Some may be vetoed.
162 bool const expectVeto =
163 (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo);
164 bool const expectObsolete =
165 (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete);
166 BEAST_EXPECTS(
167 feature.isMember(jss::enabled) && !feature[jss::enabled].asBool(),
168 feature[jss::name].asString() + " enabled");
169 BEAST_EXPECTS(
170 feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
171 (!feature[jss::vetoed].isBool() ||
172 feature[jss::vetoed].asBool() == expectVeto) &&
173 (feature[jss::vetoed].isBool() ||
174 feature[jss::vetoed].asString() == "Obsolete"),
175 feature[jss::name].asString() + " vetoed");
176 BEAST_EXPECTS(
177 feature.isMember(jss::supported) && feature[jss::supported].asBool(),
178 feature[jss::name].asString() + " supported");
179 }
180 }
181
182 void
184 {
185 testcase("Feature Param");
186
187 using namespace test::jtx;
188 Env env{*this};
189
190 std::string const name = "fixAMMOverflowOffer";
191 auto jrr = env.rpc("feature", name)[jss::result];
192 BEAST_EXPECTS(jrr[jss::status] == jss::success, "status");
193 jrr.removeMember(jss::status);
194 BEAST_EXPECT(jrr.size() == 1);
195 auto const expected = to_string(sha512Half(Slice(name.data(), name.size())));
196 char const sha[] = "12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E2A87F1D8107";
197 BEAST_EXPECT(expected == sha);
198 BEAST_EXPECT(jrr.isMember(expected));
199 auto feature = *(jrr.begin());
200
201 BEAST_EXPECTS(feature[jss::name] == name, "name");
202 BEAST_EXPECTS(!feature[jss::enabled].asBool(), "enabled");
203 BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed");
204 BEAST_EXPECTS(feature[jss::supported].asBool(), "supported");
205
206 // feature names are case-sensitive - expect error here
207 jrr = env.rpc("feature", "fMM")[jss::result];
208 BEAST_EXPECT(jrr[jss::error] == "badFeature");
209 BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
210
211 // Test feature name size checks
212 static constexpr auto kOK63Name = [] {
213 return "123456789012345678901234567890123456789012345678901234567890123";
214 };
215 static_assert(validFeatureNameSize(kOK63Name));
216
217 static constexpr auto kBaD64Name = [] {
218 return "1234567890123456789012345678901234567890123456789012345678901234";
219 };
220 static_assert(!validFeatureNameSize(kBaD64Name));
221
222 static constexpr auto kOK31Name = [] { return "1234567890123456789012345678901"; };
223 static_assert(validFeatureNameSize(kOK31Name));
224
225 static constexpr auto kBaD32Name = [] { return "12345678901234567890123456789012"; };
226 static_assert(!validFeatureNameSize(kBaD32Name));
227
228 static constexpr auto kOK33Name = [] { return "123456789012345678901234567890123"; };
229 static_assert(validFeatureNameSize(kOK33Name));
230
231 // Test feature character set checks
232 static constexpr auto kOkName = [] { return "AMM_123"; };
233 static_assert(validFeatureName(kOkName));
234
235 // First character is Greek Capital Alpha, visually confusable with ASCII 'A'
236 static constexpr auto kBadName = [] { return "ΑMM_123"; };
237 static_assert(!validFeatureName(kBadName));
238
239 static constexpr auto kBadEmoji = [] { return "🔥"; };
240 static_assert(!validFeatureName(kBadEmoji));
241 }
242
243 void
245 {
246 testcase("Invalid Feature");
247
248 using namespace test::jtx;
249 Env env{*this};
250
251 auto testInvalidParam = [&](auto const& param) {
252 json::Value params;
253 params[jss::feature] = param;
254 auto jrr = env.rpc("json", "feature", to_string(params))[jss::result];
255 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
256 BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
257 };
258
259 testInvalidParam(1);
260 testInvalidParam(1.1);
261 testInvalidParam(true);
262 testInvalidParam(json::Value(json::ValueType::Null));
263 testInvalidParam(json::Value(json::ValueType::Object));
264 testInvalidParam(json::Value(json::ValueType::Array));
265
266 {
267 auto jrr = env.rpc("feature", "AllTheThings")[jss::result];
268 BEAST_EXPECT(jrr[jss::error] == "badFeature");
269 BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
270 }
271 }
272
273 void
275 {
276 testcase("Feature Without Admin");
277
278 using namespace test::jtx;
279 Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
280 (*cfg)[Sections::kPortRpc].set(Keys::kAdmin, "");
281 (*cfg)[Sections::kPortWs].set(Keys::kAdmin, "");
282 return cfg;
283 })};
284
285 {
286 auto result = env.rpc("feature")[jss::result];
287 BEAST_EXPECT(result.isMember(jss::features));
288 // There should be at least 50 amendments. Don't do exact
289 // comparison to avoid maintenance as more amendments are added in
290 // the future.
291 BEAST_EXPECT(result[jss::features].size() >= 50);
292 for (auto it = result[jss::features].begin(); it != result[jss::features].end(); ++it)
293 {
294 uint256 id;
295 (void)id.parseHex(it.key().asString().c_str());
296 if (!BEAST_EXPECT((*it).isMember(jss::name)))
297 return;
298 bool const expectEnabled = env.app().getAmendmentTable().isEnabled(id);
299 bool const expectSupported = env.app().getAmendmentTable().isSupported(id);
300 BEAST_EXPECTS(
301 (*it).isMember(jss::enabled) && (*it)[jss::enabled].asBool() == expectEnabled,
302 (*it)[jss::name].asString() + " enabled");
303 BEAST_EXPECTS(
304 (*it).isMember(jss::supported) &&
305 (*it)[jss::supported].asBool() == expectSupported,
306 (*it)[jss::name].asString() + " supported");
307 BEAST_EXPECT(!(*it).isMember(jss::vetoed));
308 BEAST_EXPECT(!(*it).isMember(jss::majority));
309 BEAST_EXPECT(!(*it).isMember(jss::count));
310 BEAST_EXPECT(!(*it).isMember(jss::validations));
311 BEAST_EXPECT(!(*it).isMember(jss::threshold));
312 }
313 }
314
315 {
316 json::Value params;
317 // invalid feature
318 params[jss::feature] =
319 "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCD"
320 "EF";
321 auto const result = env.rpc("json", "feature", to_string(params))[jss::result];
322 BEAST_EXPECTS(result[jss::error] == "badFeature", result.toStyledString());
323 BEAST_EXPECT(result[jss::error_message] == "Feature unknown or invalid.");
324 }
325
326 {
327 json::Value params;
328 params[jss::feature] =
329 "93E516234E35E08CA689FA33A6D38E103881F8DCB53023F728C307AA89D515"
330 "A7";
331 // invalid param
332 params[jss::vetoed] = true;
333 auto const result = env.rpc("json", "feature", to_string(params))[jss::result];
334 BEAST_EXPECTS(result[jss::error] == "noPermission", result[jss::error].asString());
335 BEAST_EXPECT(
336 result[jss::error_message] == "You don't have permission for this command.");
337 }
338 }
339
340 void
342 {
343 testcase("No Params, Some Enabled");
344
345 using namespace test::jtx;
346 Env env{*this, FeatureBitset{}};
347
349
350 auto jrr = env.rpc("feature")[jss::result];
351 if (!BEAST_EXPECT(jrr.isMember(jss::features)))
352 return;
353 for (auto it = jrr[jss::features].begin(); it != jrr[jss::features].end(); ++it)
354 {
355 uint256 id;
356 (void)id.parseHex(it.key().asString().c_str());
357 if (!BEAST_EXPECT((*it).isMember(jss::name)))
358 return;
359 bool const expectEnabled = env.app().getAmendmentTable().isEnabled(id);
360 bool const expectSupported = env.app().getAmendmentTable().isSupported(id);
361 bool const expectVeto =
362 (votes.at((*it)[jss::name].asString()) == VoteBehavior::DefaultNo);
363 bool const expectObsolete =
364 (votes.at((*it)[jss::name].asString()) == VoteBehavior::Obsolete);
365 BEAST_EXPECTS(
366 (*it).isMember(jss::enabled) && (*it)[jss::enabled].asBool() == expectEnabled,
367 (*it)[jss::name].asString() + " enabled");
368 if (expectEnabled)
369 {
370 BEAST_EXPECTS(
371 !(*it).isMember(jss::vetoed), (*it)[jss::name].asString() + " vetoed");
372 }
373 else
374 {
375 BEAST_EXPECTS(
376 (*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool() == !expectObsolete &&
377 (!(*it)[jss::vetoed].isBool() ||
378 (*it)[jss::vetoed].asBool() == expectVeto) &&
379 ((*it)[jss::vetoed].isBool() ||
380 (*it)[jss::vetoed].asString() == "Obsolete"),
381 (*it)[jss::name].asString() + " vetoed");
382 }
383 BEAST_EXPECTS(
384 (*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported,
385 (*it)[jss::name].asString() + " supported");
386 }
387 }
388
389 void
391 {
392 testcase("With Majorities");
393
394 using namespace test::jtx;
395 Env env{*this, envconfig(validator, "")};
396
397 auto jrr = env.rpc("feature")[jss::result];
398 if (!BEAST_EXPECT(jrr.isMember(jss::features)))
399 return;
400
401 // at this point, there are no majorities so no fields related to
402 // amendment voting
403 for (auto const& feature : jrr[jss::features])
404 {
405 if (!BEAST_EXPECT(feature.isMember(jss::name)))
406 return;
407 BEAST_EXPECTS(
408 !feature.isMember(jss::majority), feature[jss::name].asString() + " majority");
409 BEAST_EXPECTS(!feature.isMember(jss::count), feature[jss::name].asString() + " count");
410 BEAST_EXPECTS(
411 !feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold");
412 BEAST_EXPECTS(
413 !feature.isMember(jss::validations),
414 feature[jss::name].asString() + " validations");
415 BEAST_EXPECTS(!feature.isMember(jss::vote), feature[jss::name].asString() + " vote");
416 }
417
418 auto majorities = getMajorityAmendments(*env.closed());
419 if (!BEAST_EXPECT(majorities.empty()))
420 return;
421
422 // close ledgers until the amendments show up.
423 for (auto i = 0; i <= 256; ++i)
424 {
425 env.close();
426 majorities = getMajorityAmendments(*env.closed());
427 if (!majorities.empty())
428 break;
429 }
430
431 // There should be at least 2 amendments. Don't do exact comparison
432 // to avoid maintenance as more amendments are added in the future.
433 BEAST_EXPECT(majorities.size() >= 2);
435
436 jrr = env.rpc("feature")[jss::result];
437 if (!BEAST_EXPECT(jrr.isMember(jss::features)))
438 return;
439 for (auto const& feature : jrr[jss::features])
440 {
441 if (!BEAST_EXPECT(feature.isMember(jss::name)))
442 return;
443 bool const expectVeto =
444 (votes.at(feature[jss::name].asString()) == VoteBehavior::DefaultNo);
445 bool const expectObsolete =
446 (votes.at(feature[jss::name].asString()) == VoteBehavior::Obsolete);
447 BEAST_EXPECTS(
448 (expectVeto || expectObsolete) ^ feature.isMember(jss::majority),
449 feature[jss::name].asString() + " majority");
450 BEAST_EXPECTS(
451 feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
452 (!feature[jss::vetoed].isBool() ||
453 feature[jss::vetoed].asBool() == expectVeto) &&
454 (feature[jss::vetoed].isBool() ||
455 feature[jss::vetoed].asString() == "Obsolete"),
456 feature[jss::name].asString() + " vetoed");
457 BEAST_EXPECTS(feature.isMember(jss::count), feature[jss::name].asString() + " count");
458 BEAST_EXPECTS(
459 feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold");
460 BEAST_EXPECTS(
461 feature.isMember(jss::validations), feature[jss::name].asString() + " validations");
462 BEAST_EXPECT(feature[jss::count] == ((expectVeto || expectObsolete) ? 0 : 1));
463 BEAST_EXPECT(feature[jss::threshold] == 1);
464 BEAST_EXPECT(feature[jss::validations] == 1);
465 BEAST_EXPECTS(
466 expectVeto || expectObsolete || feature[jss::majority] == 2540,
467 "Majority: " + feature[jss::majority].asString());
468 }
469 }
470
471 void
473 {
474 testcase("Veto");
475
476 using namespace test::jtx;
477 Env env{*this, FeatureBitset{featurePriceOracle}};
478 static constexpr char const* kFeatureName = "fixAMMOverflowOffer";
479
480 auto jrr = env.rpc("feature", kFeatureName)[jss::result];
481 if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
482 return;
483 jrr.removeMember(jss::status);
484 if (!BEAST_EXPECT(jrr.size() == 1))
485 return;
486 auto feature = *(jrr.begin());
487 BEAST_EXPECTS(feature[jss::name] == kFeatureName, "name");
488 BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed");
489
490 jrr = env.rpc("feature", kFeatureName, "reject")[jss::result];
491 if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
492 return;
493 jrr.removeMember(jss::status);
494 if (!BEAST_EXPECT(jrr.size() == 1))
495 return;
496 feature = *(jrr.begin());
497 BEAST_EXPECTS(feature[jss::name] == kFeatureName, "name");
498 BEAST_EXPECTS(feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool(), "vetoed");
499
500 jrr = env.rpc("feature", kFeatureName, "accept")[jss::result];
501 if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
502 return;
503 jrr.removeMember(jss::status);
504 if (!BEAST_EXPECT(jrr.size() == 1))
505 return;
506 feature = *(jrr.begin());
507 BEAST_EXPECTS(feature[jss::name] == kFeatureName, "name");
508 BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed");
509
510 // anything other than accept or reject is an error
511 jrr = env.rpc("feature", kFeatureName, "maybe");
512 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
513 BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
514 }
515
516 void
518 {
519 testcase("Obsolete");
520
521 using namespace test::jtx;
522 Env env{*this};
523
524 auto const& supportedAmendments = xrpl::detail::supportedAmendments();
525 auto obsoleteFeature = std::ranges::find_if(supportedAmendments, [](auto const& pair) {
526 return pair.second == VoteBehavior::Obsolete;
527 });
528
529 if (obsoleteFeature == std::end(supportedAmendments))
530 {
531 pass();
532 return;
533 }
534
535 auto const featureName = obsoleteFeature->first;
536
537 auto jrr = env.rpc("feature", featureName)[jss::result];
538 if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
539 return;
540 jrr.removeMember(jss::status);
541 if (!BEAST_EXPECT(jrr.size() == 1))
542 return;
543 auto feature = *(jrr.begin());
544 BEAST_EXPECTS(feature[jss::name] == featureName, "name");
545 BEAST_EXPECTS(
546 feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete",
547 "vetoed");
548
549 jrr = env.rpc("feature", featureName, "reject")[jss::result];
550 if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
551 return;
552 jrr.removeMember(jss::status);
553 if (!BEAST_EXPECT(jrr.size() == 1))
554 return;
555 feature = *(jrr.begin());
556 BEAST_EXPECTS(feature[jss::name] == featureName, "name");
557 BEAST_EXPECTS(
558 feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete",
559 "vetoed");
560
561 jrr = env.rpc("feature", featureName, "accept")[jss::result];
562 if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
563 return;
564 jrr.removeMember(jss::status);
565 if (!BEAST_EXPECT(jrr.size() == 1))
566 return;
567 feature = *(jrr.begin());
568 BEAST_EXPECTS(feature[jss::name] == featureName, "name");
569 BEAST_EXPECTS(
570 feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete",
571 "vetoed");
572
573 // anything other than accept or reject is an error
574 jrr = env.rpc("feature", featureName, "maybe");
575 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
576 BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
577 }
578
579public:
580 void
581 run() override
582 {
585 testNoParams();
588 testNonAdmin();
591 testVeto();
592 testObsolete();
593 }
594};
595
597
598} // namespace xrpl
T at(T... args)
A testsuite class.
Definition suite.h:50
void pass()
Record a successful test condition.
Definition suite.h:500
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:522
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
void run() override
Runs the suite.
An immutable linear range of bytes.
Definition Slice.h:26
T data(T... args)
T end(T... args)
T find_if(T... args)
@ Array
array value (ordered list)
Definition json_value.h:25
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
@ Null
'null' value
Definition json_value.h:19
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
std::map< std::string, VoteBehavior > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
consteval auto validFeatureNameSize(auto fn) -> bool
Definition Feature.h:80
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:204
size_t featureToBitsetIndex(uint256 const &f)
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition View.cpp:248
std::map< std::string, AmendmentSupport > const & allAmendments()
All amendments libxrpl knows about.
std::string featureToName(uint256 const &f)
uint256 bitsetIndexToFeature(size_t i)
consteval auto validFeatureName(auto fn) -> bool
Definition Feature.h:95
std::optional< uint256 > getRegisteredFeature(std::string const &name)
BaseUInt< 256 > uint256
Definition base_uint.h:562
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
T size(T... args)
static constexpr auto kAdmin
Definition Constants.h:85
static constexpr auto kPortWs
Definition Constants.h:47
static constexpr auto kPortRpc
Definition Constants.h:46