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