rippled
Feature_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2017 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/misc/AmendmentTable.h>
21 #include <ripple/protocol/Feature.h>
22 #include <ripple/protocol/jss.h>
23 #include <test/jtx.h>
24 
25 namespace ripple {
26 
27 class Feature_test : public beast::unit_test::suite
28 {
29  void
31  {
32  testcase("internals");
33 
34  std::map<std::string, VoteBehavior> const& supported =
36  BEAST_EXPECT(
37  supported.size() ==
40  std::size_t up = 0, down = 0, obsolete = 0;
41  for (std::pair<std::string const, VoteBehavior> const& amendment :
42  supported)
43  {
44  switch (amendment.second)
45  {
47  ++up;
48  break;
50  ++down;
51  break;
53  ++obsolete;
54  break;
55  default:
56  fail("Unknown VoteBehavior", __FILE__, __LINE__);
57  }
58  }
59  BEAST_EXPECT(
61  BEAST_EXPECT(up == ripple::detail::numUpVotedAmendments());
62  }
63 
64  void
66  {
67  testcase("featureToName");
68 
69  // Test all the supported features. In a perfect world, this would test
70  // FeatureCollections::featureNames, but that's private. Leave it that
71  // way.
72  auto const supported = ripple::detail::supportedAmendments();
73 
74  for (auto const& [feature, vote] : supported)
75  {
76  (void)vote;
77  auto const registered = getRegisteredFeature(feature);
78  if (BEAST_EXPECT(registered))
79  {
80  BEAST_EXPECT(featureToName(*registered) == feature);
81  BEAST_EXPECT(
83  *registered);
84  }
85  }
86 
87  // Test an arbitrary unknown feature
88  uint256 zero{0};
89  BEAST_EXPECT(featureToName(zero) == to_string(zero));
90  BEAST_EXPECT(
91  featureToName(zero) ==
92  "0000000000000000000000000000000000000000000000000000000000000000");
93 
94  // Test looking up an unknown feature
95  BEAST_EXPECT(!getRegisteredFeature("unknown"));
96 
97  // Test a random sampling of the variables. If any of these get retired
98  // or removed, swap out for any other feature.
99  BEAST_EXPECT(featureToName(featureOwnerPaysFee) == "OwnerPaysFee");
100  BEAST_EXPECT(featureToName(featureFlow) == "Flow");
101  BEAST_EXPECT(featureToName(featureNegativeUNL) == "NegativeUNL");
102  BEAST_EXPECT(featureToName(fix1578) == "fix1578");
103  BEAST_EXPECT(
105  "fixTakerDryOfferRemoval");
106  }
107 
108  void
110  {
111  testcase("No Params, None Enabled");
112 
113  using namespace test::jtx;
114  Env env{*this};
115 
118 
119  auto jrr = env.rpc("feature")[jss::result];
120  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
121  return;
122  for (auto const& feature : jrr[jss::features])
123  {
124  if (!BEAST_EXPECT(feature.isMember(jss::name)))
125  return;
126  // default config - so all should be disabled, and
127  // supported. Some may be vetoed.
128  bool expectVeto =
129  (votes.at(feature[jss::name].asString()) ==
131  bool expectObsolete =
132  (votes.at(feature[jss::name].asString()) ==
134  BEAST_EXPECTS(
135  feature.isMember(jss::enabled) &&
136  !feature[jss::enabled].asBool(),
137  feature[jss::name].asString() + " enabled");
138  BEAST_EXPECTS(
139  feature.isMember(jss::vetoed) &&
140  feature[jss::vetoed].isBool() == !expectObsolete &&
141  (!feature[jss::vetoed].isBool() ||
142  feature[jss::vetoed].asBool() == expectVeto) &&
143  (feature[jss::vetoed].isBool() ||
144  feature[jss::vetoed].asString() == "Obsolete"),
145  feature[jss::name].asString() + " vetoed");
146  BEAST_EXPECTS(
147  feature.isMember(jss::supported) &&
148  feature[jss::supported].asBool(),
149  feature[jss::name].asString() + " supported");
150  }
151  }
152 
153  void
155  {
156  testcase("Feature Param");
157 
158  using namespace test::jtx;
159  Env env{*this};
160 
161  auto jrr = env.rpc("feature", "MultiSignReserve")[jss::result];
162  BEAST_EXPECTS(jrr[jss::status] == jss::success, "status");
163  jrr.removeMember(jss::status);
164  BEAST_EXPECT(jrr.size() == 1);
165  BEAST_EXPECT(
166  jrr.isMember("586480873651E106F1D6339B0C4A8945BA705A777F3F4524626FF"
167  "1FC07EFE41D"));
168  auto feature = *(jrr.begin());
169 
170  BEAST_EXPECTS(feature[jss::name] == "MultiSignReserve", "name");
171  BEAST_EXPECTS(!feature[jss::enabled].asBool(), "enabled");
172  BEAST_EXPECTS(
173  feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(),
174  "vetoed");
175  BEAST_EXPECTS(feature[jss::supported].asBool(), "supported");
176 
177  // feature names are case-sensitive - expect error here
178  jrr = env.rpc("feature", "multiSignReserve")[jss::result];
179  BEAST_EXPECT(jrr[jss::error] == "badFeature");
180  BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
181  }
182 
183  void
185  {
186  testcase("Invalid Feature");
187 
188  using namespace test::jtx;
189  Env env{*this};
190 
191  auto jrr = env.rpc("feature", "AllTheThings")[jss::result];
192  BEAST_EXPECT(jrr[jss::error] == "badFeature");
193  BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
194  }
195 
196  void
198  {
199  testcase("Feature Without Admin");
200 
201  using namespace test::jtx;
202  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
203  (*cfg)["port_rpc"].set("admin", "");
204  (*cfg)["port_ws"].set("admin", "");
205  return cfg;
206  })};
207 
208  {
209  auto result = env.rpc("feature")[jss::result];
210  BEAST_EXPECT(result.isMember(jss::features));
211  // There should be at least 50 amendments. Don't do exact
212  // comparison to avoid maintenance as more amendments are added in
213  // the future.
214  BEAST_EXPECT(result[jss::features].size() >= 50);
215  for (auto it = result[jss::features].begin();
216  it != result[jss::features].end();
217  ++it)
218  {
219  uint256 id;
220  (void)id.parseHex(it.key().asString().c_str());
221  if (!BEAST_EXPECT((*it).isMember(jss::name)))
222  return;
223  bool expectEnabled =
224  env.app().getAmendmentTable().isEnabled(id);
225  bool expectSupported =
226  env.app().getAmendmentTable().isSupported(id);
227  BEAST_EXPECTS(
228  (*it).isMember(jss::enabled) &&
229  (*it)[jss::enabled].asBool() == expectEnabled,
230  (*it)[jss::name].asString() + " enabled");
231  BEAST_EXPECTS(
232  (*it).isMember(jss::supported) &&
233  (*it)[jss::supported].asBool() == expectSupported,
234  (*it)[jss::name].asString() + " supported");
235  BEAST_EXPECT(!(*it).isMember(jss::vetoed));
236  BEAST_EXPECT(!(*it).isMember(jss::majority));
237  BEAST_EXPECT(!(*it).isMember(jss::count));
238  BEAST_EXPECT(!(*it).isMember(jss::validations));
239  BEAST_EXPECT(!(*it).isMember(jss::threshold));
240  }
241  }
242 
243  {
244  Json::Value params;
245  // invalid feature
246  params[jss::feature] =
247  "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCD"
248  "EF";
249  auto const result = env.rpc(
250  "json",
251  "feature",
252  boost::lexical_cast<std::string>(params))[jss::result];
253  BEAST_EXPECTS(
254  result[jss::error] == "badFeature", result.toStyledString());
255  BEAST_EXPECT(
256  result[jss::error_message] == "Feature unknown or invalid.");
257  }
258 
259  {
260  Json::Value params;
261  params[jss::feature] =
262  "93E516234E35E08CA689FA33A6D38E103881F8DCB53023F728C307AA89D515"
263  "A7";
264  // invalid param
265  params[jss::vetoed] = true;
266  auto const result = env.rpc(
267  "json",
268  "feature",
269  boost::lexical_cast<std::string>(params))[jss::result];
270  BEAST_EXPECTS(
271  result[jss::error] == "noPermission",
272  result[jss::error].asString());
273  BEAST_EXPECT(
274  result[jss::error_message] ==
275  "You don't have permission for this command.");
276  }
277 
278  {
279  std::string const feature =
280  "C4483A1896170C66C098DEA5B0E024309C60DC960DE5F01CD7AF986AA3D9AD"
281  "37";
282  Json::Value params;
283  params[jss::feature] = feature;
284  auto const result = env.rpc(
285  "json",
286  "feature",
287  boost::lexical_cast<std::string>(params))[jss::result];
288  BEAST_EXPECT(result.isMember(feature));
289  auto const amendmentResult = result[feature];
290  BEAST_EXPECT(amendmentResult[jss::enabled].asBool() == false);
291  BEAST_EXPECT(amendmentResult[jss::supported].asBool() == true);
292  BEAST_EXPECT(
293  amendmentResult[jss::name].asString() ==
294  "fixMasterKeyAsRegularKey");
295  }
296  }
297 
298  void
300  {
301  testcase("No Params, Some Enabled");
302 
303  using namespace test::jtx;
304  Env env{
306 
309 
310  auto jrr = env.rpc("feature")[jss::result];
311  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
312  return;
313  for (auto it = jrr[jss::features].begin();
314  it != jrr[jss::features].end();
315  ++it)
316  {
317  uint256 id;
318  (void)id.parseHex(it.key().asString().c_str());
319  if (!BEAST_EXPECT((*it).isMember(jss::name)))
320  return;
321  bool expectEnabled = env.app().getAmendmentTable().isEnabled(id);
322  bool expectSupported =
323  env.app().getAmendmentTable().isSupported(id);
324  bool expectVeto =
325  (votes.at((*it)[jss::name].asString()) ==
327  bool expectObsolete =
328  (votes.at((*it)[jss::name].asString()) ==
330  BEAST_EXPECTS(
331  (*it).isMember(jss::enabled) &&
332  (*it)[jss::enabled].asBool() == expectEnabled,
333  (*it)[jss::name].asString() + " enabled");
334  if (expectEnabled)
335  BEAST_EXPECTS(
336  !(*it).isMember(jss::vetoed),
337  (*it)[jss::name].asString() + " vetoed");
338  else
339  BEAST_EXPECTS(
340  (*it).isMember(jss::vetoed) &&
341  (*it)[jss::vetoed].isBool() == !expectObsolete &&
342  (!(*it)[jss::vetoed].isBool() ||
343  (*it)[jss::vetoed].asBool() == expectVeto) &&
344  ((*it)[jss::vetoed].isBool() ||
345  (*it)[jss::vetoed].asString() == "Obsolete"),
346  (*it)[jss::name].asString() + " vetoed");
347  BEAST_EXPECTS(
348  (*it).isMember(jss::supported) &&
349  (*it)[jss::supported].asBool() == expectSupported,
350  (*it)[jss::name].asString() + " supported");
351  }
352  }
353 
354  void
356  {
357  testcase("With Majorities");
358 
359  using namespace test::jtx;
360  Env env{*this, envconfig(validator, "")};
361 
362  auto jrr = env.rpc("feature")[jss::result];
363  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
364  return;
365 
366  // at this point, there are no majorities so no fields related to
367  // amendment voting
368  for (auto const& feature : jrr[jss::features])
369  {
370  if (!BEAST_EXPECT(feature.isMember(jss::name)))
371  return;
372  BEAST_EXPECTS(
373  !feature.isMember(jss::majority),
374  feature[jss::name].asString() + " majority");
375  BEAST_EXPECTS(
376  !feature.isMember(jss::count),
377  feature[jss::name].asString() + " count");
378  BEAST_EXPECTS(
379  !feature.isMember(jss::threshold),
380  feature[jss::name].asString() + " threshold");
381  BEAST_EXPECTS(
382  !feature.isMember(jss::validations),
383  feature[jss::name].asString() + " validations");
384  BEAST_EXPECTS(
385  !feature.isMember(jss::vote),
386  feature[jss::name].asString() + " vote");
387  }
388 
389  auto majorities = getMajorityAmendments(*env.closed());
390  if (!BEAST_EXPECT(majorities.empty()))
391  return;
392 
393  // close ledgers until the amendments show up.
394  for (auto i = 0; i <= 256; ++i)
395  {
396  env.close();
397  majorities = getMajorityAmendments(*env.closed());
398  if (!majorities.empty())
399  break;
400  }
401 
402  // There should be at least 5 amendments. Don't do exact comparison
403  // to avoid maintenance as more amendments are added in the future.
404  BEAST_EXPECT(majorities.size() >= 5);
407 
408  jrr = env.rpc("feature")[jss::result];
409  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
410  return;
411  for (auto const& feature : jrr[jss::features])
412  {
413  if (!BEAST_EXPECT(feature.isMember(jss::name)))
414  return;
415  bool expectVeto =
416  (votes.at(feature[jss::name].asString()) ==
418  bool expectObsolete =
419  (votes.at(feature[jss::name].asString()) ==
421  BEAST_EXPECTS(
422  (expectVeto || expectObsolete) ^
423  feature.isMember(jss::majority),
424  feature[jss::name].asString() + " majority");
425  BEAST_EXPECTS(
426  feature.isMember(jss::vetoed) &&
427  feature[jss::vetoed].isBool() == !expectObsolete &&
428  (!feature[jss::vetoed].isBool() ||
429  feature[jss::vetoed].asBool() == expectVeto) &&
430  (feature[jss::vetoed].isBool() ||
431  feature[jss::vetoed].asString() == "Obsolete"),
432  feature[jss::name].asString() + " vetoed");
433  BEAST_EXPECTS(
434  feature.isMember(jss::count),
435  feature[jss::name].asString() + " count");
436  BEAST_EXPECTS(
437  feature.isMember(jss::threshold),
438  feature[jss::name].asString() + " threshold");
439  BEAST_EXPECTS(
440  feature.isMember(jss::validations),
441  feature[jss::name].asString() + " validations");
442  BEAST_EXPECT(
443  feature[jss::count] ==
444  ((expectVeto || expectObsolete) ? 0 : 1));
445  BEAST_EXPECT(feature[jss::threshold] == 1);
446  BEAST_EXPECT(feature[jss::validations] == 1);
447  BEAST_EXPECTS(
448  expectVeto || expectObsolete || feature[jss::majority] == 2540,
449  "Majority: " + feature[jss::majority].asString());
450  }
451  }
452 
453  void
455  {
456  testcase("Veto");
457 
458  using namespace test::jtx;
459  Env env{*this, FeatureBitset(featureMultiSignReserve)};
460  constexpr const char* featureName = "MultiSignReserve";
461 
462  auto jrr = env.rpc("feature", featureName)[jss::result];
463  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
464  return;
465  jrr.removeMember(jss::status);
466  if (!BEAST_EXPECT(jrr.size() == 1))
467  return;
468  auto feature = *(jrr.begin());
469  BEAST_EXPECTS(feature[jss::name] == featureName, "name");
470  BEAST_EXPECTS(
471  feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(),
472  "vetoed");
473 
474  jrr = env.rpc("feature", featureName, "reject")[jss::result];
475  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
476  return;
477  jrr.removeMember(jss::status);
478  if (!BEAST_EXPECT(jrr.size() == 1))
479  return;
480  feature = *(jrr.begin());
481  BEAST_EXPECTS(feature[jss::name] == featureName, "name");
482  BEAST_EXPECTS(
483  feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool(),
484  "vetoed");
485 
486  jrr = env.rpc("feature", featureName, "accept")[jss::result];
487  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
488  return;
489  jrr.removeMember(jss::status);
490  if (!BEAST_EXPECT(jrr.size() == 1))
491  return;
492  feature = *(jrr.begin());
493  BEAST_EXPECTS(feature[jss::name] == featureName, "name");
494  BEAST_EXPECTS(
495  feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(),
496  "vetoed");
497 
498  // anything other than accept or reject is an error
499  jrr = env.rpc("feature", featureName, "maybe");
500  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
501  BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
502  }
503 
504  void
506  {
507  testcase("Obsolete");
508 
509  using namespace test::jtx;
510  Env env{*this};
511  constexpr const char* featureName = "NonFungibleTokensV1";
512 
513  auto jrr = env.rpc("feature", featureName)[jss::result];
514  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
515  return;
516  jrr.removeMember(jss::status);
517  if (!BEAST_EXPECT(jrr.size() == 1))
518  return;
519  auto feature = *(jrr.begin());
520  BEAST_EXPECTS(feature[jss::name] == featureName, "name");
521  BEAST_EXPECTS(
522  feature[jss::vetoed].isString() &&
523  feature[jss::vetoed].asString() == "Obsolete",
524  "vetoed");
525 
526  jrr = env.rpc("feature", featureName, "reject")[jss::result];
527  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
528  return;
529  jrr.removeMember(jss::status);
530  if (!BEAST_EXPECT(jrr.size() == 1))
531  return;
532  feature = *(jrr.begin());
533  BEAST_EXPECTS(feature[jss::name] == featureName, "name");
534  BEAST_EXPECTS(
535  feature[jss::vetoed].isString() &&
536  feature[jss::vetoed].asString() == "Obsolete",
537  "vetoed");
538 
539  jrr = env.rpc("feature", featureName, "accept")[jss::result];
540  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
541  return;
542  jrr.removeMember(jss::status);
543  if (!BEAST_EXPECT(jrr.size() == 1))
544  return;
545  feature = *(jrr.begin());
546  BEAST_EXPECTS(feature[jss::name] == featureName, "name");
547  BEAST_EXPECTS(
548  feature[jss::vetoed].isString() &&
549  feature[jss::vetoed].asString() == "Obsolete",
550  "vetoed");
551 
552  // anything other than accept or reject is an error
553  jrr = env.rpc("feature", featureName, "maybe");
554  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
555  BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
556  }
557 
558 public:
559  void
560  run() override
561  {
562  testInternals();
564  testNoParams();
567  testNonAdmin();
568  testSomeEnabled();
570  testVeto();
571  testObsolete();
572  }
573 };
574 
575 BEAST_DEFINE_TESTSUITE(Feature, rpc, ripple);
576 
577 } // namespace ripple
ripple::getMajorityAmendments
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:638
ripple::AmendmentVote::up
@ up
ripple::Feature_test::testSingleFeature
void testSingleFeature()
Definition: Feature_test.cpp:154
std::string
STL class.
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
ripple::Feature_test::testVeto
void testVeto()
Definition: Feature_test.cpp:454
ripple::AmendmentVote::obsolete
@ obsolete
std::pair
ripple::featureDepositPreauth
const uint256 featureDepositPreauth
ripple::VoteBehavior::DefaultNo
@ DefaultNo
ripple::Feature_test::run
void run() override
Definition: Feature_test.cpp:560
ripple::Feature_test::testFeatureLookups
void testFeatureLookups()
Definition: Feature_test.cpp:65
ripple::featureDepositAuth
const uint256 featureDepositAuth
ripple::featureMultiSignReserve
const uint256 featureMultiSignReserve
ripple::VoteBehavior::Obsolete
@ Obsolete
ripple::detail::numUpVotedAmendments
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition: Feature.cpp:334
ripple::AmendmentVote::down
@ down
ripple::base_uint< 256 >
ripple::detail::numDownVotedAmendments
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition: Feature.cpp:327
ripple::Feature_test::testSomeEnabled
void testSomeEnabled()
Definition: Feature_test.cpp:299
ripple::Feature_test
Definition: Feature_test.cpp:27
std::map::at
T at(T... args)
ripple::fixTakerDryOfferRemoval
const uint256 fixTakerDryOfferRemoval
ripple::featureToBitsetIndex
size_t featureToBitsetIndex(uint256 const &f)
Definition: Feature.cpp:369
std::map
STL class.
ripple::Feature_test::testInternals
void testInternals()
Definition: Feature_test.cpp:30
ripple::fix1578
const uint256 fix1578
ripple::Feature_test::testWithMajorities
void testWithMajorities()
Definition: Feature_test.cpp:355
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::featureFlow
const uint256 featureFlow
ripple::featureNegativeUNL
const uint256 featureNegativeUNL
ripple::bitsetIndexToFeature
uint256 bitsetIndexToFeature(size_t i)
Definition: Feature.cpp:375
ripple::FeatureBitset
Definition: Feature.h:113
ripple::Feature_test::testNoParams
void testNoParams()
Definition: Feature_test.cpp:109
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:38
ripple::featureOwnerPaysFee
const uint256 featureOwnerPaysFee
ripple::Feature_test::testNonAdmin
void testNonAdmin()
Definition: Feature_test.cpp:197
ripple::getRegisteredFeature
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:342
ripple::Feature_test::testInvalidFeature
void testInvalidFeature()
Definition: Feature_test.cpp:184
std::unique_ptr
STL class.
ripple::detail::supportedAmendments
std::map< std::string, VoteBehavior > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition: Feature.cpp:320
ripple::Feature_test::testObsolete
void testObsolete()
Definition: Feature_test.cpp:505
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::featureToName
std::string featureToName(uint256 const &f)
Definition: Feature.cpp:381
ripple::VoteBehavior::DefaultYes
@ DefaultYes