rippled
AmendmentTable_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 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/basics/BasicConfig.h>
22 #include <ripple/basics/Log.h>
23 #include <ripple/basics/chrono.h>
24 #include <ripple/beast/unit_test.h>
25 #include <ripple/core/ConfigSections.h>
26 #include <ripple/protocol/Feature.h>
27 #include <ripple/protocol/PublicKey.h>
28 #include <ripple/protocol/STValidation.h>
29 #include <ripple/protocol/SecretKey.h>
30 #include <ripple/protocol/TxFlags.h>
31 #include <ripple/protocol/digest.h>
32 #include <ripple/protocol/jss.h>
33 #include <test/jtx/Env.h>
34 #include <test/unit_test/SuiteJournal.h>
35 
36 namespace ripple {
37 
38 class AmendmentTable_test final : public beast::unit_test::suite
39 {
40 private:
41  static uint256
43  {
44  sha256_hasher h;
45  using beast::hash_append;
46  hash_append(h, in);
47  auto const d = static_cast<sha256_hasher::result_type>(h);
48  uint256 result;
49  std::memcpy(result.data(), d.data(), d.size());
50  return result;
51  }
52 
53  static Section
55  std::string const& name,
56  std::vector<std::string> const& amendments)
57  {
58  Section section(name);
59  for (auto const& a : amendments)
60  section.append(to_string(amendmentId(a)) + " " + a);
61  return section;
62  }
63 
64  static Section
66  {
67  return makeSection("Test", amendments);
68  }
69 
70  static Section
71  makeSection(uint256 const& amendment)
72  {
73  Section section("Test");
74  section.append(to_string(amendment) + " " + to_string(amendment));
75  return section;
76  }
77 
80  {
81  auto cfg = test::jtx::envconfig();
82  cfg->section(SECTION_AMENDMENTS) =
83  makeSection(SECTION_AMENDMENTS, enabled_);
84  cfg->section(SECTION_VETO_AMENDMENTS) =
85  makeSection(SECTION_VETO_AMENDMENTS, vetoed_);
86  return cfg;
87  }
88 
91  std::vector<std::string> const& amendments,
92  VoteBehavior voteBehavior)
93  {
95  result.reserve(amendments.size());
96  for (auto const& a : amendments)
97  {
98  result.emplace_back(a, amendmentId(a), voteBehavior);
99  }
100  return result;
101  }
102 
105  {
106  return makeFeatureInfo(amendments, VoteBehavior::DefaultYes);
107  }
108 
110  makeDefaultYes(uint256 const amendment)
111  {
113  {to_string(amendment), amendment, VoteBehavior::DefaultYes}};
114  return result;
115  }
116 
119  {
120  return makeFeatureInfo(amendments, VoteBehavior::DefaultNo);
121  }
122 
125  {
126  return makeFeatureInfo(amendments, VoteBehavior::Obsolete);
127  }
128 
129  template <class Arg, class... Args>
130  static size_t
131  totalsize(std::vector<Arg> const& src, Args const&... args)
132  {
133  if constexpr (sizeof...(args) > 0)
134  return src.size() + totalsize(args...);
135  return src.size();
136  }
137 
138  template <class Arg, class... Args>
139  static void
141  std::vector<Arg>& dest,
142  std::vector<Arg> const& src,
143  Args const&... args)
144  {
145  assert(dest.capacity() >= dest.size() + src.size());
146  std::copy(src.begin(), src.end(), std::back_inserter(dest));
147  if constexpr (sizeof...(args) > 0)
148  combine_arg(dest, args...);
149  }
150 
151  template <class Arg, class... Args>
152  static std::vector<Arg>
154  // Pass "left" by value. The values will need to be copied one way or
155  // another, so just reuse it.
156  std::vector<Arg> left,
157  std::vector<Arg> const& right,
158  Args const&... args)
159  {
160  left.reserve(totalsize(left, right, args...));
161 
162  combine_arg(left, right, args...);
163 
164  return left;
165  }
166 
167  // All useful amendments are supported amendments.
168  // Enabled amendments are typically a subset of supported amendments.
169  // Vetoed amendments should be supported but not enabled.
170  // Unsupported amendments may be added to the AmendmentTable.
172  yes_{"g", "i", "k", "m", "o", "q", "r", "s", "t", "u"};
174  enabled_{"b", "d", "f", "h", "j", "l", "n", "p"};
175  std::vector<std::string> const vetoed_{"a", "c", "e"};
176  std::vector<std::string> const obsolete_{"0", "1", "2"};
181 
184 
186 
187 public:
188  AmendmentTable_test() : journal_("AmendmentTable_test", *this)
189  {
190  }
191 
194  Application& app,
195  std::chrono::seconds majorityTime,
197  Section const& enabled,
198  Section const& vetoed)
199  {
200  return make_AmendmentTable(
201  app, majorityTime, supported, enabled, vetoed, journal_);
202  }
203 
206  test::jtx::Env& env,
207  std::chrono::seconds majorityTime,
209  Section const& enabled,
210  Section const& vetoed)
211  {
212  return makeTable(env.app(), majorityTime, supported, enabled, vetoed);
213  }
214 
217  {
218  static std::vector<AmendmentTable::FeatureInfo> const supported =
219  combine(
221  // Use non-intuitive default votes for "enabled_" and "vetoed_"
222  // so that when the tests later explicitly enable or veto them,
223  // we can be certain that they are not simply going by their
224  // default vote setting.
228  return makeTable(
229  env.app(),
230  majorityTime,
231  supported,
234  }
235 
236  void
238  {
239  testcase("Construction");
240  test::jtx::Env env{*this, makeConfig()};
241  auto table = makeTable(env, weeks(1));
242 
243  for (auto const& a : allSupported_)
244  BEAST_EXPECT(table->isSupported(amendmentId(a)));
245 
246  for (auto const& a : yes_)
247  BEAST_EXPECT(table->isSupported(amendmentId(a)));
248 
249  for (auto const& a : enabled_)
250  BEAST_EXPECT(table->isSupported(amendmentId(a)));
251 
252  for (auto const& a : vetoed_)
253  {
254  BEAST_EXPECT(table->isSupported(amendmentId(a)));
255  BEAST_EXPECT(!table->isEnabled(amendmentId(a)));
256  }
257 
258  for (auto const& a : obsolete_)
259  {
260  BEAST_EXPECT(table->isSupported(amendmentId(a)));
261  BEAST_EXPECT(!table->isEnabled(amendmentId(a)));
262  }
263  }
264 
265  void
267  {
268  testcase("Name to ID mapping");
269 
270  test::jtx::Env env{*this, makeConfig()};
271  auto table = makeTable(env, weeks(1));
272 
273  for (auto const& a : yes_)
274  BEAST_EXPECT(table->find(a) == amendmentId(a));
275  for (auto const& a : enabled_)
276  BEAST_EXPECT(table->find(a) == amendmentId(a));
277  for (auto const& a : vetoed_)
278  BEAST_EXPECT(table->find(a) == amendmentId(a));
279  for (auto const& a : obsolete_)
280  BEAST_EXPECT(table->find(a) == amendmentId(a));
281  for (auto const& a : unsupported_)
282  BEAST_EXPECT(!table->find(a));
283  for (auto const& a : unsupportedMajority_)
284  BEAST_EXPECT(!table->find(a));
285 
286  // Vetoing an unsupported amendment should add the amendment to table.
287  // Verify that unsupportedID is not in table.
288  uint256 const unsupportedID = amendmentId(unsupported_[0]);
289  {
290  Json::Value const unsupp =
291  table->getJson(unsupportedID)[to_string(unsupportedID)];
292  BEAST_EXPECT(unsupp.size() == 0);
293  }
294 
295  // After vetoing unsupportedID verify that it is in table.
296  table->veto(unsupportedID);
297  {
298  Json::Value const unsupp =
299  table->getJson(unsupportedID)[to_string(unsupportedID)];
300  BEAST_EXPECT(unsupp[jss::vetoed].asBool());
301  }
302  }
303 
304  void
306  {
307  auto const yesVotes = makeDefaultYes(yes_);
308  auto const section = makeSection(vetoed_);
309  auto const id = to_string(amendmentId(enabled_[0]));
310 
311  testcase("Bad Config");
312 
313  { // Two arguments are required - we pass one
314  Section test = section;
315  test.append(id);
316 
317  try
318  {
319  test::jtx::Env env{*this, makeConfig()};
320  if (makeTable(env, weeks(2), yesVotes, test, emptySection_))
321  fail("Accepted only amendment ID");
322  }
323  catch (std::exception const& e)
324  {
325  BEAST_EXPECT(
326  e.what() == "Invalid entry '" + id + "' in [Test]");
327  }
328  }
329 
330  { // Two arguments are required - we pass three
331  Section test = section;
332  test.append(id + " Test Name");
333 
334  try
335  {
336  test::jtx::Env env{*this, makeConfig()};
337  if (makeTable(env, weeks(2), yesVotes, test, emptySection_))
338  fail("Accepted extra arguments");
339  }
340  catch (std::exception const& e)
341  {
342  BEAST_EXPECT(
343  e.what() ==
344  "Invalid entry '" + id + " Test Name' in [Test]");
345  }
346  }
347 
348  {
349  auto sid = id;
350  sid.resize(sid.length() - 1);
351 
352  Section test = section;
353  test.append(sid + " Name");
354 
355  try
356  {
357  test::jtx::Env env{*this, makeConfig()};
358  if (makeTable(env, weeks(2), yesVotes, test, emptySection_))
359  fail("Accepted short amendment ID");
360  }
361  catch (std::exception const& e)
362  {
363  BEAST_EXPECT(
364  e.what() == "Invalid entry '" + sid + " Name' in [Test]");
365  }
366  }
367 
368  {
369  auto sid = id;
370  sid.resize(sid.length() + 1, '0');
371 
372  Section test = section;
373  test.append(sid + " Name");
374 
375  try
376  {
377  test::jtx::Env env{*this, makeConfig()};
378  if (makeTable(env, weeks(2), yesVotes, test, emptySection_))
379  fail("Accepted long amendment ID");
380  }
381  catch (std::exception const& e)
382  {
383  BEAST_EXPECT(
384  e.what() == "Invalid entry '" + sid + " Name' in [Test]");
385  }
386  }
387 
388  {
389  auto sid = id;
390  sid.resize(sid.length() - 1);
391  sid.push_back('Q');
392 
393  Section test = section;
394  test.append(sid + " Name");
395 
396  try
397  {
398  test::jtx::Env env{*this, makeConfig()};
399  if (makeTable(env, weeks(2), yesVotes, test, emptySection_))
400  fail("Accepted non-hex amendment ID");
401  }
402  catch (std::exception const& e)
403  {
404  BEAST_EXPECT(
405  e.what() == "Invalid entry '" + sid + " Name' in [Test]");
406  }
407  }
408  }
409 
410  void
412  {
413  testcase("enable and veto");
414 
415  test::jtx::Env env{*this, makeConfig()};
417 
418  // Note which entries are enabled (convert the amendment names to IDs)
419  std::set<uint256> allEnabled;
420  for (auto const& a : enabled_)
421  allEnabled.insert(amendmentId(a));
422 
423  for (uint256 const& a : allEnabled)
424  BEAST_EXPECT(table->enable(a));
425 
426  // So far all enabled amendments are supported.
427  BEAST_EXPECT(!table->hasUnsupportedEnabled());
428 
429  // Verify all enables are enabled and nothing else.
430  for (std::string const& a : yes_)
431  {
432  uint256 const supportedID = amendmentId(a);
433  bool const enabled = table->isEnabled(supportedID);
434  bool const found = allEnabled.find(supportedID) != allEnabled.end();
435  BEAST_EXPECTS(
436  enabled == found,
437  a + (enabled ? " enabled " : " disabled ") +
438  (found ? " found" : " not found"));
439  }
440 
441  // All supported and unVetoed amendments should be returned as desired.
442  {
443  std::set<uint256> vetoed;
444  for (std::string const& a : vetoed_)
445  vetoed.insert(amendmentId(a));
446 
447  std::vector<uint256> const desired = table->getDesired();
448  for (uint256 const& a : desired)
449  BEAST_EXPECT(vetoed.count(a) == 0);
450 
451  // Unveto an amendment that is already not vetoed. Shouldn't
452  // hurt anything, but the values returned by getDesired()
453  // shouldn't change.
454  BEAST_EXPECT(!table->unVeto(amendmentId(yes_[1])));
455  BEAST_EXPECT(desired == table->getDesired());
456  }
457 
458  // UnVeto one of the vetoed amendments. It should now be desired.
459  {
460  uint256 const unvetoedID = amendmentId(vetoed_[0]);
461  BEAST_EXPECT(table->unVeto(unvetoedID));
462 
463  std::vector<uint256> const desired = table->getDesired();
464  BEAST_EXPECT(
465  std::find(desired.begin(), desired.end(), unvetoedID) !=
466  desired.end());
467  }
468 
469  // Veto all supported amendments. Now desired should be empty.
470  for (std::string const& a : allSupported_)
471  {
472  table->veto(amendmentId(a));
473  }
474  BEAST_EXPECT(table->getDesired().empty());
475 
476  // Enable an unsupported amendment.
477  {
478  BEAST_EXPECT(!table->hasUnsupportedEnabled());
479  table->enable(amendmentId(unsupported_[0]));
480  BEAST_EXPECT(table->hasUnsupportedEnabled());
481  }
482  }
483 
484  // Make a list of trusted validators.
485  // Register the validators with AmendmentTable and return the list.
488  {
490  ret.reserve(num);
491  hash_set<PublicKey> trustedValidators;
492  trustedValidators.reserve(num);
493  for (int i = 0; i < num; ++i)
494  {
495  auto const& back =
497  trustedValidators.insert(back.first);
498  }
499  table->trustChanged(trustedValidators);
500  return ret;
501  }
502 
503  static NetClock::time_point
505  {
506  return NetClock::time_point{h};
507  }
508 
509  // Execute a pretend consensus round for a flag ledger
510  void
512  Rules const& rules,
513  AmendmentTable& table,
514  std::chrono::hours hour,
515  std::vector<std::pair<PublicKey, SecretKey>> const& validators,
516  std::vector<std::pair<uint256, int>> const& votes,
517  std::vector<uint256>& ourVotes,
518  std::set<uint256>& enabled,
519  majorityAmendments_t& majority)
520  {
521  // Do a round at the specified time
522  // Returns the amendments we voted for
523 
524  // Parameters:
525  // table: Our table of known and vetoed amendments
526  // validators: The addreses of validators we trust
527  // votes: Amendments and the number of validators who vote for them
528  // ourVotes: The amendments we vote for in our validation
529  // enabled: In/out enabled amendments
530  // majority: In/our majority amendments (and when they got a majority)
531 
532  auto const roundTime = hourTime(hour);
533 
534  // Build validations
536  validations.reserve(validators.size());
537 
538  int i = 0;
539  for (auto const& [pub, sec] : validators)
540  {
541  ++i;
542  std::vector<uint256> field;
543 
544  for (auto const& [hash, nVotes] : votes)
545  {
546  if (rules.enabled(fixAmendmentMajorityCalc) ? nVotes >= i
547  : nVotes > i)
548  {
549  // We vote yes on this amendment
550  field.push_back(hash);
551  }
552  }
553 
554  auto v = std::make_shared<STValidation>(
556  pub,
557  sec,
558  calcNodeID(pub),
559  [&field](STValidation& v) {
560  if (!field.empty())
561  v.setFieldV256(
563  v.setFieldU32(sfLedgerSequence, 6180339);
564  });
565 
566  validations.emplace_back(v);
567  }
568 
569  ourVotes = table.doValidation(enabled);
570 
571  auto actions =
572  table.doVoting(rules, roundTime, enabled, majority, validations);
573  for (auto const& [hash, action] : actions)
574  {
575  // This code assumes other validators do as we do
576 
577  switch (action)
578  {
579  case 0:
580  // amendment goes from majority to enabled
581  if (enabled.find(hash) != enabled.end())
582  Throw<std::runtime_error>("enabling already enabled");
583  if (majority.find(hash) == majority.end())
584  Throw<std::runtime_error>("enabling without majority");
585  enabled.insert(hash);
586  majority.erase(hash);
587  break;
588 
589  case tfGotMajority:
590  if (majority.find(hash) != majority.end())
591  Throw<std::runtime_error>(
592  "got majority while having majority");
593  majority[hash] = roundTime;
594  break;
595 
596  case tfLostMajority:
597  if (majority.find(hash) == majority.end())
598  Throw<std::runtime_error>(
599  "lost majority without majority");
600  majority.erase(hash);
601  break;
602 
603  default:
604  Throw<std::runtime_error>("unknown action");
605  }
606  }
607  }
608 
609  // No vote on unknown amendment
610  void
612  {
613  testcase("Vote NO on unknown");
614 
615  auto const testAmendment = amendmentId("TestAmendment");
616 
617  test::jtx::Env env{*this, feat};
618  auto table =
620 
621  auto const validators = makeValidators(10, table);
622 
624  std::vector<uint256> ourVotes;
625  std::set<uint256> enabled;
626  majorityAmendments_t majority;
627 
628  doRound(
629  env.current()->rules(),
630  *table,
631  weeks{1},
632  validators,
633  votes,
634  ourVotes,
635  enabled,
636  majority);
637  BEAST_EXPECT(ourVotes.empty());
638  BEAST_EXPECT(enabled.empty());
639  BEAST_EXPECT(majority.empty());
640 
641  votes.emplace_back(testAmendment, validators.size());
642 
643  doRound(
644  env.current()->rules(),
645  *table,
646  weeks{2},
647  validators,
648  votes,
649  ourVotes,
650  enabled,
651  majority);
652  BEAST_EXPECT(ourVotes.empty());
653  BEAST_EXPECT(enabled.empty());
654 
655  majority[testAmendment] = hourTime(weeks{1});
656 
657  // Note that the simulation code assumes others behave as we do,
658  // so the amendment won't get enabled
659  doRound(
660  env.current()->rules(),
661  *table,
662  weeks{5},
663  validators,
664  votes,
665  ourVotes,
666  enabled,
667  majority);
668  BEAST_EXPECT(ourVotes.empty());
669  BEAST_EXPECT(enabled.empty());
670  }
671 
672  // No vote on vetoed amendment
673  void
675  {
676  testcase("Vote NO on vetoed");
677 
678  auto const testAmendment = amendmentId("vetoedAmendment");
679 
680  test::jtx::Env env{*this, feat};
681  auto table = makeTable(
682  env,
683  weeks(2),
684  emptyYes_,
686  makeSection(testAmendment));
687 
688  auto const validators = makeValidators(10, table);
689 
691  std::vector<uint256> ourVotes;
692  std::set<uint256> enabled;
693  majorityAmendments_t majority;
694 
695  doRound(
696  env.current()->rules(),
697  *table,
698  weeks{1},
699  validators,
700  votes,
701  ourVotes,
702  enabled,
703  majority);
704  BEAST_EXPECT(ourVotes.empty());
705  BEAST_EXPECT(enabled.empty());
706  BEAST_EXPECT(majority.empty());
707 
708  votes.emplace_back(testAmendment, validators.size());
709 
710  doRound(
711  env.current()->rules(),
712  *table,
713  weeks{2},
714  validators,
715  votes,
716  ourVotes,
717  enabled,
718  majority);
719  BEAST_EXPECT(ourVotes.empty());
720  BEAST_EXPECT(enabled.empty());
721 
722  majority[testAmendment] = hourTime(weeks{1});
723 
724  doRound(
725  env.current()->rules(),
726  *table,
727  weeks{5},
728  validators,
729  votes,
730  ourVotes,
731  enabled,
732  majority);
733  BEAST_EXPECT(ourVotes.empty());
734  BEAST_EXPECT(enabled.empty());
735  }
736 
737  // Vote on and enable known, not-enabled amendment
738  void
740  {
741  testcase("voteEnable");
742 
743  test::jtx::Env env{*this, feat};
744  auto table = makeTable(
746 
747  auto const validators = makeValidators(10, table);
748 
750  std::vector<uint256> ourVotes;
751  std::set<uint256> enabled;
752  majorityAmendments_t majority;
753 
754  // Week 1: We should vote for all known amendments not enabled
755  doRound(
756  env.current()->rules(),
757  *table,
758  weeks{1},
759  validators,
760  votes,
761  ourVotes,
762  enabled,
763  majority);
764  BEAST_EXPECT(ourVotes.size() == yes_.size());
765  BEAST_EXPECT(enabled.empty());
766  for (auto const& i : yes_)
767  BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end());
768 
769  // Now, everyone votes for this feature
770  for (auto const& i : yes_)
771  votes.emplace_back(amendmentId(i), validators.size());
772 
773  // Week 2: We should recognize a majority
774  doRound(
775  env.current()->rules(),
776  *table,
777  weeks{2},
778  validators,
779  votes,
780  ourVotes,
781  enabled,
782  majority);
783  BEAST_EXPECT(ourVotes.size() == yes_.size());
784  BEAST_EXPECT(enabled.empty());
785 
786  for (auto const& i : yes_)
787  BEAST_EXPECT(majority[amendmentId(i)] == hourTime(weeks{2}));
788 
789  // Week 5: We should enable the amendment
790  doRound(
791  env.current()->rules(),
792  *table,
793  weeks{5},
794  validators,
795  votes,
796  ourVotes,
797  enabled,
798  majority);
799  BEAST_EXPECT(enabled.size() == yes_.size());
800 
801  // Week 6: We should remove it from our votes and from having a majority
802  doRound(
803  env.current()->rules(),
804  *table,
805  weeks{6},
806  validators,
807  votes,
808  ourVotes,
809  enabled,
810  majority);
811  BEAST_EXPECT(enabled.size() == yes_.size());
812  BEAST_EXPECT(ourVotes.empty());
813  for (auto const& i : yes_)
814  BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end());
815  }
816 
817  // Detect majority at 80%, enable later
818  void
820  {
821  testcase("detectMajority");
822 
823  auto const testAmendment = amendmentId("detectMajority");
824  test::jtx::Env env{*this, feat};
825  auto table = makeTable(
826  env,
827  weeks(2),
828  makeDefaultYes(testAmendment),
830  emptySection_);
831 
832  auto const validators = makeValidators(16, table);
833 
834  std::set<uint256> enabled;
835  majorityAmendments_t majority;
836 
837  for (int i = 0; i <= 17; ++i)
838  {
840  std::vector<uint256> ourVotes;
841 
842  if ((i > 0) && (i < 17))
843  votes.emplace_back(testAmendment, i);
844 
845  doRound(
846  env.current()->rules(),
847  *table,
848  weeks{i},
849  validators,
850  votes,
851  ourVotes,
852  enabled,
853  majority);
854 
855  if (i < 13) // 13 => 13/16 = 0.8125 => > 80%
856  {
857  // We are voting yes, not enabled, no majority
858  BEAST_EXPECT(!ourVotes.empty());
859  BEAST_EXPECT(enabled.empty());
860  BEAST_EXPECT(majority.empty());
861  }
862  else if (i < 15)
863  {
864  // We have a majority, not enabled, keep voting
865  BEAST_EXPECT(!ourVotes.empty());
866  BEAST_EXPECT(!majority.empty());
867  BEAST_EXPECT(enabled.empty());
868  }
869  else if (i == 15)
870  {
871  // enable, keep voting, remove from majority
872  BEAST_EXPECT(!ourVotes.empty());
873  BEAST_EXPECT(majority.empty());
874  BEAST_EXPECT(!enabled.empty());
875  }
876  else
877  {
878  // Done, we should be enabled and not voting
879  BEAST_EXPECT(ourVotes.empty());
880  BEAST_EXPECT(majority.empty());
881  BEAST_EXPECT(!enabled.empty());
882  }
883  }
884  }
885 
886  // Detect loss of majority
887  void
889  {
890  testcase("lostMajority");
891 
892  auto const testAmendment = amendmentId("lostMajority");
893 
894  test::jtx::Env env{*this, feat};
895  auto table = makeTable(
896  env,
897  weeks(8),
898  makeDefaultYes(testAmendment),
900  emptySection_);
901 
902  auto const validators = makeValidators(16, table);
903 
904  std::set<uint256> enabled;
905  majorityAmendments_t majority;
906 
907  {
908  // establish majority
910  std::vector<uint256> ourVotes;
911 
912  votes.emplace_back(testAmendment, validators.size());
913 
914  doRound(
915  env.current()->rules(),
916  *table,
917  weeks{1},
918  validators,
919  votes,
920  ourVotes,
921  enabled,
922  majority);
923 
924  BEAST_EXPECT(enabled.empty());
925  BEAST_EXPECT(!majority.empty());
926  }
927 
928  for (int i = 1; i < 8; ++i)
929  {
931  std::vector<uint256> ourVotes;
932 
933  // Gradually reduce support
934  votes.emplace_back(testAmendment, validators.size() - i);
935 
936  doRound(
937  env.current()->rules(),
938  *table,
939  weeks{i + 1},
940  validators,
941  votes,
942  ourVotes,
943  enabled,
944  majority);
945 
946  if (i < 4) // 16 - 3 = 13 => 13/16 = 0.8125 => > 80%
947  { // 16 - 4 = 12 => 12/16 = 0.75 => < 80%
948  // We are voting yes, not enabled, majority
949  BEAST_EXPECT(!ourVotes.empty());
950  BEAST_EXPECT(enabled.empty());
951  BEAST_EXPECT(!majority.empty());
952  }
953  else
954  {
955  // No majority, not enabled, keep voting
956  BEAST_EXPECT(!ourVotes.empty());
957  BEAST_EXPECT(majority.empty());
958  BEAST_EXPECT(enabled.empty());
959  }
960  }
961  }
962 
963  // Exercise the UNL changing while voting is in progress.
964  void
966  {
967  // This test doesn't work without fixAmendmentMajorityCalc enabled.
968  if (!feat[fixAmendmentMajorityCalc])
969  return;
970 
971  testcase("changedUNL");
972 
973  auto const testAmendment = amendmentId("changedUNL");
974  test::jtx::Env env{*this, feat};
975  auto table = makeTable(
976  env,
977  weeks(8),
978  makeDefaultYes(testAmendment),
980  emptySection_);
981 
983  makeValidators(10, table);
984 
985  std::set<uint256> enabled;
986  majorityAmendments_t majority;
987 
988  {
989  // 10 validators with 2 voting against won't get majority.
991  std::vector<uint256> ourVotes;
992 
993  votes.emplace_back(testAmendment, validators.size() - 2);
994 
995  doRound(
996  env.current()->rules(),
997  *table,
998  weeks{1},
999  validators,
1000  votes,
1001  ourVotes,
1002  enabled,
1003  majority);
1004 
1005  BEAST_EXPECT(enabled.empty());
1006  BEAST_EXPECT(majority.empty());
1007  }
1008 
1009  // Add one new validator to the UNL.
1011 
1012  // A lambda that updates the AmendmentTable with the latest
1013  // trusted validators.
1014  auto callTrustChanged =
1015  [](std::vector<std::pair<PublicKey, SecretKey>> const& validators,
1016  std::unique_ptr<AmendmentTable> const& table) {
1017  // We need a hash_set to pass to trustChanged.
1018  hash_set<PublicKey> trustedValidators;
1019  trustedValidators.reserve(validators.size());
1020  std::for_each(
1021  validators.begin(),
1022  validators.end(),
1023  [&trustedValidators](auto const& val) {
1024  trustedValidators.insert(val.first);
1025  });
1026 
1027  // Tell the AmendmentTable that the UNL changed.
1028  table->trustChanged(trustedValidators);
1029  };
1030 
1031  // Tell the table that there's been a change in trusted validators.
1032  callTrustChanged(validators, table);
1033 
1034  {
1035  // 11 validators with 2 voting against gains majority.
1037  std::vector<uint256> ourVotes;
1038 
1039  votes.emplace_back(testAmendment, validators.size() - 2);
1040 
1041  doRound(
1042  env.current()->rules(),
1043  *table,
1044  weeks{2},
1045  validators,
1046  votes,
1047  ourVotes,
1048  enabled,
1049  majority);
1050 
1051  BEAST_EXPECT(enabled.empty());
1052  BEAST_EXPECT(!majority.empty());
1053  }
1054  {
1055  // One of the validators goes flaky and doesn't send validations
1056  // (without the UNL changing) so the amendment loses majority.
1057  std::pair<PublicKey, SecretKey> const savedValidator =
1058  validators.front();
1059  validators.erase(validators.begin());
1060 
1062  std::vector<uint256> ourVotes;
1063 
1064  votes.emplace_back(testAmendment, validators.size() - 2);
1065 
1066  doRound(
1067  env.current()->rules(),
1068  *table,
1069  weeks{3},
1070  validators,
1071  votes,
1072  ourVotes,
1073  enabled,
1074  majority);
1075 
1076  BEAST_EXPECT(enabled.empty());
1077  BEAST_EXPECT(majority.empty());
1078 
1079  // Simulate the validator re-syncing to the network by adding it
1080  // back to the validators vector
1081  validators.insert(validators.begin(), savedValidator);
1082 
1083  votes.front().second = validators.size() - 2;
1084 
1085  doRound(
1086  env.current()->rules(),
1087  *table,
1088  weeks{4},
1089  validators,
1090  votes,
1091  ourVotes,
1092  enabled,
1093  majority);
1094 
1095  BEAST_EXPECT(enabled.empty());
1096  BEAST_EXPECT(!majority.empty());
1097 
1098  // Finally, remove one validator from the UNL and see that majority
1099  // is lost.
1100  validators.erase(validators.begin());
1101 
1102  // Tell the table that there's been a change in trusted validators.
1103  callTrustChanged(validators, table);
1104 
1105  votes.front().second = validators.size() - 2;
1106 
1107  doRound(
1108  env.current()->rules(),
1109  *table,
1110  weeks{5},
1111  validators,
1112  votes,
1113  ourVotes,
1114  enabled,
1115  majority);
1116 
1117  BEAST_EXPECT(enabled.empty());
1118  BEAST_EXPECT(majority.empty());
1119  }
1120  }
1121 
1122  // Exercise a validator losing connectivity and then regaining it after
1123  // extended delays. Depending on how long that delay is an amendment
1124  // either will or will not go live.
1125  void
1127  {
1128  // This test doesn't work without fixAmendmentMajorityCalc enabled.
1129  if (!feat[fixAmendmentMajorityCalc])
1130  return;
1131 
1132  testcase("validatorFlapping");
1133 
1134  // We run a test where a validator flaps on and off every 23 hours
1135  // and another one one where it flaps on and off every 25 hours.
1136  //
1137  // Since the local validator vote record expires after 24 hours,
1138  // with 23 hour flapping the amendment will go live. But with 25
1139  // hour flapping the amendment will not go live.
1140  for (int flapRateHours : {23, 25})
1141  {
1142  test::jtx::Env env{*this, feat};
1143  auto const testAmendment = amendmentId("validatorFlapping");
1144  auto table = makeTable(
1145  env,
1146  weeks(1),
1147  makeDefaultYes(testAmendment),
1148  emptySection_,
1149  emptySection_);
1150 
1151  // Make two lists of validators, one with a missing validator, to
1152  // make it easy to simulate validator flapping.
1153  auto const allValidators = makeValidators(11, table);
1154  decltype(allValidators) const mostValidators(
1155  allValidators.begin() + 1, allValidators.end());
1156  BEAST_EXPECT(allValidators.size() == mostValidators.size() + 1);
1157 
1158  std::set<uint256> enabled;
1159  majorityAmendments_t majority;
1160 
1162  std::vector<uint256> ourVotes;
1163 
1164  votes.emplace_back(testAmendment, allValidators.size() - 2);
1165 
1166  int delay = flapRateHours;
1167  // Loop for 1 week plus a day.
1168  for (int hour = 1; hour < (24 * 8); ++hour)
1169  {
1170  decltype(allValidators) const& thisHoursValidators =
1171  (delay < flapRateHours) ? mostValidators : allValidators;
1172  delay = delay == flapRateHours ? 0 : delay + 1;
1173 
1174  votes.front().second = thisHoursValidators.size() - 2;
1175 
1176  using namespace std::chrono;
1177  doRound(
1178  env.current()->rules(),
1179  *table,
1180  hours(hour),
1181  thisHoursValidators,
1182  votes,
1183  ourVotes,
1184  enabled,
1185  majority);
1186 
1187  if (hour <= (24 * 7) || flapRateHours > 24)
1188  {
1189  // The amendment should not be enabled under any
1190  // circumstance until one week has elapsed.
1191  BEAST_EXPECT(enabled.empty());
1192 
1193  // If flapping is less than 24 hours, there should be
1194  // no flapping. Otherwise we should only have majority
1195  // if allValidators vote -- which means there are no
1196  // missing validators.
1197  bool const expectMajority = (delay <= 24)
1198  ? true
1199  : &thisHoursValidators == &allValidators;
1200  BEAST_EXPECT(majority.empty() != expectMajority);
1201  }
1202  else
1203  {
1204  // We're...
1205  // o Past one week, and
1206  // o AmendmentFlapping was less than 24 hours.
1207  // The amendment should be enabled.
1208  BEAST_EXPECT(!enabled.empty());
1209  BEAST_EXPECT(majority.empty());
1210  }
1211  }
1212  }
1213  }
1214 
1215  void
1217  {
1218  testcase("hasUnsupportedEnabled");
1219 
1220  using namespace std::chrono_literals;
1221  weeks constexpr w(1);
1222  test::jtx::Env env{*this, makeConfig()};
1223  auto table = makeTable(env, w);
1224  BEAST_EXPECT(!table->hasUnsupportedEnabled());
1225  BEAST_EXPECT(!table->firstUnsupportedExpected());
1226  BEAST_EXPECT(table->needValidatedLedger(1));
1227 
1228  std::set<uint256> enabled;
1229  std::for_each(
1230  unsupported_.begin(),
1231  unsupported_.end(),
1232  [&enabled](auto const& s) { enabled.insert(amendmentId(s)); });
1233 
1234  majorityAmendments_t majority;
1235  table->doValidatedLedger(1, enabled, majority);
1236  BEAST_EXPECT(table->hasUnsupportedEnabled());
1237  BEAST_EXPECT(!table->firstUnsupportedExpected());
1238 
1239  NetClock::duration t{1000s};
1240  std::for_each(
1243  [&majority, &t](auto const& s) {
1244  majority[amendmentId(s)] = NetClock::time_point{--t};
1245  });
1246 
1247  table->doValidatedLedger(1, enabled, majority);
1248  BEAST_EXPECT(table->hasUnsupportedEnabled());
1249  BEAST_EXPECT(
1250  table->firstUnsupportedExpected() &&
1251  *table->firstUnsupportedExpected() == NetClock::time_point{t} + w);
1252 
1253  // Make sure the table knows when it needs an update.
1254  BEAST_EXPECT(!table->needValidatedLedger(256));
1255  BEAST_EXPECT(table->needValidatedLedger(257));
1256  }
1257 
1258  void
1260  {
1261  testNoOnUnknown(feat);
1262  testNoOnVetoed(feat);
1263  testVoteEnable(feat);
1264  testDetectMajority(feat);
1265  testLostMajority(feat);
1266  testChangedUNL(feat);
1267  testValidatorFlapping(feat);
1268  }
1269 
1270  void
1271  run() override
1272  {
1274  FeatureBitset const fixMajorityCalc{fixAmendmentMajorityCalc};
1275 
1276  testConstruct();
1277  testGet();
1278  testBadConfig();
1279  testEnableVeto();
1281  testFeature(all - fixMajorityCalc);
1282  testFeature(all);
1283  }
1284 };
1285 
1286 BEAST_DEFINE_TESTSUITE(AmendmentTable, app, ripple);
1287 
1288 } // namespace ripple
ripple::tfGotMajority
constexpr std::uint32_t tfGotMajority
Definition: TxFlags.h:119
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
ripple::Application
Definition: Application.h:116
ripple::AmendmentTable_test::allSupported_
const std::vector< std::string > allSupported_
Definition: AmendmentTable_test.cpp:177
ripple::tfLostMajority
constexpr std::uint32_t tfLostMajority
Definition: TxFlags.h:120
std::for_each
T for_each(T... args)
ripple::AmendmentTable_test::makeDefaultNo
static std::vector< AmendmentTable::FeatureInfo > makeDefaultNo(std::vector< std::string > const &amendments)
Definition: AmendmentTable_test.cpp:118
ripple::AmendmentTable_test::unsupported_
const std::vector< std::string > unsupported_
Definition: AmendmentTable_test.cpp:179
ripple::openssl_sha256_hasher
SHA-256 digest.
Definition: digest.h:90
ripple::AmendmentTable::doVoting
virtual std::map< uint256, std::uint32_t > doVoting(Rules const &rules, NetClock::time_point closeTime, std::set< uint256 > const &enabledAmendments, majorityAmendments_t const &majorityAmendments, std::vector< std::shared_ptr< STValidation >> const &valSet)=0
std::string
STL class.
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
ripple::AmendmentTable_test::hourTime
static NetClock::time_point hourTime(std::chrono::hours h)
Definition: AmendmentTable_test.cpp:504
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
std::exception
STL class.
ripple::calcNodeID
NodeID calcNodeID(PublicKey const &pk)
Calculate the 160-bit node ID from a node public key.
Definition: PublicKey.cpp:303
ripple::AmendmentTable_test::makeSection
static Section makeSection(uint256 const &amendment)
Definition: AmendmentTable_test.cpp:71
ripple::sfLedgerSequence
const SF_UINT32 sfLedgerSequence
ripple::AmendmentTable_test::testValidatorFlapping
void testValidatorFlapping(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:1126
std::unordered_set
STL class.
std::pair
std::vector::reserve
T reserve(T... args)
ripple::TxSearched::all
@ all
ripple::AmendmentTable_test::testConstruct
void testConstruct()
Definition: AmendmentTable_test.cpp:237
std::vector< std::string >
std::find
T find(T... args)
std::vector::size
T size(T... args)
ripple::VoteBehavior::DefaultNo
@ DefaultNo
std::back_inserter
T back_inserter(T... args)
std::chrono::seconds
ripple::AmendmentTable_test::makeDefaultYes
static std::vector< AmendmentTable::FeatureInfo > makeDefaultYes(uint256 const amendment)
Definition: AmendmentTable_test.cpp:110
ripple::AmendmentTable_test::testChangedUNL
void testChangedUNL(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:965
ripple::QualityDirection::in
@ in
ripple::AmendmentTable_test::testNoOnUnknown
void testNoOnUnknown(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:611
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
ripple::AmendmentTable_test::testHasUnsupported
void testHasUnsupported()
Definition: AmendmentTable_test.cpp:1216
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
std::vector::front
T front(T... args)
ripple::base_uint::data
pointer data()
Definition: base_uint.h:122
ripple::STValidation
Definition: STValidation.h:44
ripple::VoteBehavior::Obsolete
@ Obsolete
ripple::base_uint::size
constexpr static std::size_t size()
Definition: base_uint.h:519
ripple::Section::append
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
Definition: BasicConfig.cpp:38
ripple::AmendmentTable_test::makeValidators
std::vector< std::pair< PublicKey, SecretKey > > makeValidators(int num, std::unique_ptr< AmendmentTable > const &table)
Definition: AmendmentTable_test.cpp:487
ripple::AmendmentTable_test::emptySection_
const Section emptySection_
Definition: AmendmentTable_test.cpp:182
ripple::AmendmentTable_test::unsupportedMajority_
const std::vector< std::string > unsupportedMajority_
Definition: AmendmentTable_test.cpp:180
ripple::base_uint< 256 >
std::vector::capacity
T capacity(T... args)
ripple::AmendmentTable::doValidation
virtual std::vector< uint256 > doValidation(std::set< uint256 > const &enabled) const =0
ripple::AmendmentTable_test::testEnableVeto
void testEnableVeto()
Definition: AmendmentTable_test.cpp:411
ripple::weeks
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > >> weeks
Definition: chrono.h:44
ripple::AmendmentTable_test::testNoOnVetoed
void testNoOnVetoed(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:674
ripple::VoteBehavior
VoteBehavior
Definition: Feature.h:69
ripple::AmendmentTable_test::totalsize
static size_t totalsize(std::vector< Arg > const &src, Args const &... args)
Definition: AmendmentTable_test.cpp:131
ripple::AmendmentTable_test::makeTable
std::unique_ptr< AmendmentTable > makeTable(test::jtx::Env &env, std::chrono::seconds majorityTime)
Definition: AmendmentTable_test.cpp:216
ripple::fixAmendmentMajorityCalc
const uint256 fixAmendmentMajorityCalc
std::array< std::uint8_t, 32 >
std::chrono::time_point
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
std::map::erase
T erase(T... args)
std::copy
T copy(T... args)
ripple::AmendmentTable_test::testFeature
void testFeature(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:1259
ripple::AmendmentTable_test::yes_
const std::vector< std::string > yes_
Definition: AmendmentTable_test.cpp:172
ripple::test::jtx::supported_amendments
FeatureBitset supported_amendments()
Definition: Env.h:70
ripple::AmendmentTable_test::makeObsolete
static std::vector< AmendmentTable::FeatureInfo > makeObsolete(std::vector< std::string > const &amendments)
Definition: AmendmentTable_test.cpp:124
std::map
STL class.
ripple::AmendmentTable_test::vetoed_
const std::vector< std::string > vetoed_
Definition: AmendmentTable_test.cpp:175
ripple::test::SuiteJournal
Definition: SuiteJournal.h:88
ripple::KeyType::secp256k1
@ secp256k1
ripple::randomKeyPair
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:368
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::AmendmentTable_test::testGet
void testGet()
Definition: AmendmentTable_test.cpp:266
ripple::AmendmentTable_test::emptyYes_
const std::vector< AmendmentTable::FeatureInfo > emptyYes_
Definition: AmendmentTable_test.cpp:183
ripple::AmendmentTable_test::makeTable
std::unique_ptr< AmendmentTable > makeTable(test::jtx::Env &env, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed)
Definition: AmendmentTable_test.cpp:205
std::vector::begin
T begin(T... args)
ripple::AmendmentTable_test::obsolete_
const std::vector< std::string > obsolete_
Definition: AmendmentTable_test.cpp:176
std::set::insert
T insert(T... args)
beast::hash_append
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
Definition: hash_append.h:236
ripple::AmendmentTable_test::journal_
test::SuiteJournal journal_
Definition: AmendmentTable_test.cpp:185
ripple::AmendmentTable_test::makeSection
static Section makeSection(std::vector< std::string > const &amendments)
Definition: AmendmentTable_test.cpp:65
ripple::AmendmentTable_test::makeConfig
std::unique_ptr< Config > makeConfig()
Definition: AmendmentTable_test.cpp:79
ripple::FeatureBitset
Definition: Feature.h:113
std::set::count
T count(T... args)
ripple::STVector256
Definition: STVector256.h:30
ripple::AmendmentTable_test::makeSection
static Section makeSection(std::string const &name, std::vector< std::string > const &amendments)
Definition: AmendmentTable_test.cpp:54
std::vector::empty
T empty(T... args)
ripple::Rules
Rules controlling protocol behavior.
Definition: Rules.h:33
ripple::AmendmentTable_test::combine
static std::vector< Arg > combine(std::vector< Arg > left, std::vector< Arg > const &right, Args const &... args)
Definition: AmendmentTable_test.cpp:153
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:41
std::memcpy
T memcpy(T... args)
ripple::make_AmendmentTable
std::unique_ptr< AmendmentTable > make_AmendmentTable(Application &app, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
Definition: AmendmentTable.cpp:973
ripple::AmendmentTable_test::makeFeatureInfo
static std::vector< AmendmentTable::FeatureInfo > makeFeatureInfo(std::vector< std::string > const &amendments, VoteBehavior voteBehavior)
Definition: AmendmentTable_test.cpp:90
std::vector::end
T end(T... args)
ripple::AmendmentTable_test::testVoteEnable
void testVoteEnable(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:739
ripple::AmendmentTable_test::testDetectMajority
void testDetectMajority(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:819
ripple::AmendmentTable_test::testLostMajority
void testLostMajority(FeatureBitset const &feat)
Definition: AmendmentTable_test.cpp:888
ripple::AmendmentTable_test
Definition: AmendmentTable_test.cpp:38
ripple::AmendmentTable_test::makeTable
std::unique_ptr< AmendmentTable > makeTable(Application &app, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed)
Definition: AmendmentTable_test.cpp:193
ripple::AmendmentTable_test::makeDefaultYes
static std::vector< AmendmentTable::FeatureInfo > makeDefaultYes(std::vector< std::string > const &amendments)
Definition: AmendmentTable_test.cpp:104
ripple::hash_append
void hash_append(Hasher &h, ValidatorBlobInfo const &blobInfo)
Definition: ValidatorList.h:897
ripple::AmendmentTable
The amendment table stores the list of enabled and potential amendments.
Definition: AmendmentTable.h:37
std::unique_ptr
STL class.
ripple::AmendmentTable_test::doRound
void doRound(Rules const &rules, AmendmentTable &table, std::chrono::hours hour, std::vector< std::pair< PublicKey, SecretKey >> const &validators, std::vector< std::pair< uint256, int >> const &votes, std::vector< uint256 > &ourVotes, std::set< uint256 > &enabled, majorityAmendments_t &majority)
Definition: AmendmentTable_test.cpp:511
ripple::AmendmentTable_test::AmendmentTable_test
AmendmentTable_test()
Definition: AmendmentTable_test.cpp:188
ripple::AmendmentTable_test::testBadConfig
void testBadConfig()
Definition: AmendmentTable_test.cpp:305
std::set
STL class.
ripple::AmendmentTable_test::run
void run() override
Definition: AmendmentTable_test.cpp:1271
ripple::AmendmentTable_test::amendmentId
static uint256 amendmentId(std::string in)
Definition: AmendmentTable_test.cpp:42
ripple::AmendmentTable_test::enabled_
const std::vector< std::string > enabled_
Definition: AmendmentTable_test.cpp:174
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
std::exception::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::sfAmendments
const SF_VECTOR256 sfAmendments
ripple::AmendmentTable_test::combine_arg
static void combine_arg(std::vector< Arg > &dest, std::vector< Arg > const &src, Args const &... args)
Definition: AmendmentTable_test.cpp:140
std::chrono
ripple::VoteBehavior::DefaultYes
@ DefaultYes