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 
485  makeValidators(int num)
486  {
488  ret.reserve(num);
489  for (int i = 0; i < num; ++i)
490  {
492  }
493  return ret;
494  }
495 
496  static NetClock::time_point
498  {
499  return NetClock::time_point{w};
500  }
501 
502  // Execute a pretend consensus round for a flag ledger
503  void
505  uint256 const& feat,
506  AmendmentTable& table,
507  weeks week,
508  std::vector<std::pair<PublicKey, SecretKey>> const& validators,
509  std::vector<std::pair<uint256, int>> const& votes,
510  std::vector<uint256>& ourVotes,
511  std::set<uint256>& enabled,
512  majorityAmendments_t& majority)
513  {
514  // Do a round at the specified time
515  // Returns the amendments we voted for
516 
517  // Parameters:
518  // table: Our table of known and vetoed amendments
519  // validators: The addreses of validators we trust
520  // votes: Amendments and the number of validators who vote for them
521  // ourVotes: The amendments we vote for in our validation
522  // enabled: In/out enabled amendments
523  // majority: In/our majority amendments (and when they got a majority)
524 
525  auto const roundTime = weekTime(week);
526 
527  // Build validations
529  validations.reserve(validators.size());
530 
531  int i = 0;
532  for (auto const& [pub, sec] : validators)
533  {
534  ++i;
535  std::vector<uint256> field;
536 
537  for (auto const& [hash, nVotes] : votes)
538  {
539  if (feat == fixAmendmentMajorityCalc ? nVotes >= i : nVotes > i)
540  {
541  // We vote yes on this amendment
542  field.push_back(hash);
543  }
544  }
545 
546  auto v = std::make_shared<STValidation>(
548  pub,
549  sec,
550  calcNodeID(pub),
551  [&field](STValidation& v) {
552  if (!field.empty())
553  v.setFieldV256(
555  v.setFieldU32(sfLedgerSequence, 6180339);
556  });
557 
558  validations.emplace_back(v);
559  }
560 
561  ourVotes = table.doValidation(enabled);
562 
563  auto actions = table.doVoting(
564  Rules({feat}), roundTime, enabled, majority, validations);
565  for (auto const& [hash, action] : actions)
566  {
567  // This code assumes other validators do as we do
568 
569  switch (action)
570  {
571  case 0:
572  // amendment goes from majority to enabled
573  if (enabled.find(hash) != enabled.end())
574  Throw<std::runtime_error>("enabling already enabled");
575  if (majority.find(hash) == majority.end())
576  Throw<std::runtime_error>("enabling without majority");
577  enabled.insert(hash);
578  majority.erase(hash);
579  break;
580 
581  case tfGotMajority:
582  if (majority.find(hash) != majority.end())
583  Throw<std::runtime_error>(
584  "got majority while having majority");
585  majority[hash] = roundTime;
586  break;
587 
588  case tfLostMajority:
589  if (majority.find(hash) == majority.end())
590  Throw<std::runtime_error>(
591  "lost majority without majority");
592  majority.erase(hash);
593  break;
594 
595  default:
596  Throw<std::runtime_error>("unknown action");
597  }
598  }
599  }
600 
601  // No vote on unknown amendment
602  void
604  {
605  testcase("Vote NO on unknown");
606 
607  auto const testAmendment = amendmentId("TestAmendment");
608  auto const validators = makeValidators(10);
609 
610  test::jtx::Env env{*this};
611  auto table =
613 
615  std::vector<uint256> ourVotes;
616  std::set<uint256> enabled;
617  majorityAmendments_t majority;
618 
619  doRound(
620  feat,
621  *table,
622  weeks{1},
623  validators,
624  votes,
625  ourVotes,
626  enabled,
627  majority);
628  BEAST_EXPECT(ourVotes.empty());
629  BEAST_EXPECT(enabled.empty());
630  BEAST_EXPECT(majority.empty());
631 
632  votes.emplace_back(testAmendment, validators.size());
633 
634  doRound(
635  feat,
636  *table,
637  weeks{2},
638  validators,
639  votes,
640  ourVotes,
641  enabled,
642  majority);
643  BEAST_EXPECT(ourVotes.empty());
644  BEAST_EXPECT(enabled.empty());
645 
646  majority[testAmendment] = weekTime(weeks{1});
647 
648  // Note that the simulation code assumes others behave as we do,
649  // so the amendment won't get enabled
650  doRound(
651  feat,
652  *table,
653  weeks{5},
654  validators,
655  votes,
656  ourVotes,
657  enabled,
658  majority);
659  BEAST_EXPECT(ourVotes.empty());
660  BEAST_EXPECT(enabled.empty());
661  }
662 
663  // No vote on vetoed amendment
664  void
665  testNoOnVetoed(uint256 const& feat)
666  {
667  testcase("Vote NO on vetoed");
668 
669  auto const testAmendment = amendmentId("vetoedAmendment");
670 
671  test::jtx::Env env{*this};
672  auto table = makeTable(
673  env,
674  weeks(2),
675  emptyYes_,
677  makeSection(testAmendment));
678 
679  auto const validators = makeValidators(10);
680 
682  std::vector<uint256> ourVotes;
683  std::set<uint256> enabled;
684  majorityAmendments_t majority;
685 
686  doRound(
687  feat,
688  *table,
689  weeks{1},
690  validators,
691  votes,
692  ourVotes,
693  enabled,
694  majority);
695  BEAST_EXPECT(ourVotes.empty());
696  BEAST_EXPECT(enabled.empty());
697  BEAST_EXPECT(majority.empty());
698 
699  votes.emplace_back(testAmendment, validators.size());
700 
701  doRound(
702  feat,
703  *table,
704  weeks{2},
705  validators,
706  votes,
707  ourVotes,
708  enabled,
709  majority);
710  BEAST_EXPECT(ourVotes.empty());
711  BEAST_EXPECT(enabled.empty());
712 
713  majority[testAmendment] = weekTime(weeks{1});
714 
715  doRound(
716  feat,
717  *table,
718  weeks{5},
719  validators,
720  votes,
721  ourVotes,
722  enabled,
723  majority);
724  BEAST_EXPECT(ourVotes.empty());
725  BEAST_EXPECT(enabled.empty());
726  }
727 
728  // Vote on and enable known, not-enabled amendment
729  void
730  testVoteEnable(uint256 const& feat)
731  {
732  testcase("voteEnable");
733 
734  test::jtx::Env env{*this};
735  auto table = makeTable(
737 
738  auto const validators = makeValidators(10);
740  std::vector<uint256> ourVotes;
741  std::set<uint256> enabled;
742  majorityAmendments_t majority;
743 
744  // Week 1: We should vote for all known amendments not enabled
745  doRound(
746  feat,
747  *table,
748  weeks{1},
749  validators,
750  votes,
751  ourVotes,
752  enabled,
753  majority);
754  BEAST_EXPECT(ourVotes.size() == yes_.size());
755  BEAST_EXPECT(enabled.empty());
756  for (auto const& i : yes_)
757  BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end());
758 
759  // Now, everyone votes for this feature
760  for (auto const& i : yes_)
761  votes.emplace_back(amendmentId(i), validators.size());
762 
763  // Week 2: We should recognize a majority
764  doRound(
765  feat,
766  *table,
767  weeks{2},
768  validators,
769  votes,
770  ourVotes,
771  enabled,
772  majority);
773  BEAST_EXPECT(ourVotes.size() == yes_.size());
774  BEAST_EXPECT(enabled.empty());
775 
776  for (auto const& i : yes_)
777  BEAST_EXPECT(majority[amendmentId(i)] == weekTime(weeks{2}));
778 
779  // Week 5: We should enable the amendment
780  doRound(
781  feat,
782  *table,
783  weeks{5},
784  validators,
785  votes,
786  ourVotes,
787  enabled,
788  majority);
789  BEAST_EXPECT(enabled.size() == yes_.size());
790 
791  // Week 6: We should remove it from our votes and from having a majority
792  doRound(
793  feat,
794  *table,
795  weeks{6},
796  validators,
797  votes,
798  ourVotes,
799  enabled,
800  majority);
801  BEAST_EXPECT(enabled.size() == yes_.size());
802  BEAST_EXPECT(ourVotes.empty());
803  for (auto const& i : yes_)
804  BEAST_EXPECT(majority.find(amendmentId(i)) == majority.end());
805  }
806 
807  // Detect majority at 80%, enable later
808  void
810  {
811  testcase("detectMajority");
812 
813  auto const testAmendment = amendmentId("detectMajority");
814  test::jtx::Env env{*this};
815  auto table = makeTable(
816  env,
817  weeks(2),
818  makeDefaultYes(testAmendment),
820  emptySection_);
821 
822  auto const validators = makeValidators(16);
823 
824  std::set<uint256> enabled;
825  majorityAmendments_t majority;
826 
827  for (int i = 0; i <= 17; ++i)
828  {
830  std::vector<uint256> ourVotes;
831 
832  if ((i > 0) && (i < 17))
833  votes.emplace_back(testAmendment, i);
834 
835  doRound(
836  feat,
837  *table,
838  weeks{i},
839  validators,
840  votes,
841  ourVotes,
842  enabled,
843  majority);
844 
845  if (i < 13) // 13 => 13/16 = 0.8125 => > 80%
846  {
847  // We are voting yes, not enabled, no majority
848  BEAST_EXPECT(!ourVotes.empty());
849  BEAST_EXPECT(enabled.empty());
850  BEAST_EXPECT(majority.empty());
851  }
852  else if (i < 15)
853  {
854  // We have a majority, not enabled, keep voting
855  BEAST_EXPECT(!ourVotes.empty());
856  BEAST_EXPECT(!majority.empty());
857  BEAST_EXPECT(enabled.empty());
858  }
859  else if (i == 15)
860  {
861  // enable, keep voting, remove from majority
862  BEAST_EXPECT(!ourVotes.empty());
863  BEAST_EXPECT(majority.empty());
864  BEAST_EXPECT(!enabled.empty());
865  }
866  else
867  {
868  // Done, we should be enabled and not voting
869  BEAST_EXPECT(ourVotes.empty());
870  BEAST_EXPECT(majority.empty());
871  BEAST_EXPECT(!enabled.empty());
872  }
873  }
874  }
875 
876  // Detect loss of majority
877  void
879  {
880  testcase("lostMajority");
881 
882  auto const testAmendment = amendmentId("lostMajority");
883  auto const validators = makeValidators(16);
884 
885  test::jtx::Env env{*this};
886  auto table = makeTable(
887  env,
888  weeks(8),
889  makeDefaultYes(testAmendment),
891  emptySection_);
892 
893  std::set<uint256> enabled;
894  majorityAmendments_t majority;
895 
896  {
897  // establish majority
899  std::vector<uint256> ourVotes;
900 
901  votes.emplace_back(testAmendment, validators.size());
902 
903  doRound(
904  feat,
905  *table,
906  weeks{1},
907  validators,
908  votes,
909  ourVotes,
910  enabled,
911  majority);
912 
913  BEAST_EXPECT(enabled.empty());
914  BEAST_EXPECT(!majority.empty());
915  }
916 
917  for (int i = 1; i < 8; ++i)
918  {
920  std::vector<uint256> ourVotes;
921 
922  // Gradually reduce support
923  votes.emplace_back(testAmendment, validators.size() - i);
924 
925  doRound(
926  feat,
927  *table,
928  weeks{i + 1},
929  validators,
930  votes,
931  ourVotes,
932  enabled,
933  majority);
934 
935  if (i < 4) // 16 - 3 = 13 => 13/16 = 0.8125 => > 80%
936  { // 16 - 4 = 12 => 12/16 = 0.75 => < 80%
937  // We are voting yes, not enabled, majority
938  BEAST_EXPECT(!ourVotes.empty());
939  BEAST_EXPECT(enabled.empty());
940  BEAST_EXPECT(!majority.empty());
941  }
942  else
943  {
944  // No majority, not enabled, keep voting
945  BEAST_EXPECT(!ourVotes.empty());
946  BEAST_EXPECT(majority.empty());
947  BEAST_EXPECT(enabled.empty());
948  }
949  }
950  }
951 
952  void
954  {
955  testcase("hasUnsupportedEnabled");
956 
957  using namespace std::chrono_literals;
958  weeks constexpr w(1);
959  test::jtx::Env env{*this, makeConfig()};
960  auto table = makeTable(env, w);
961  BEAST_EXPECT(!table->hasUnsupportedEnabled());
962  BEAST_EXPECT(!table->firstUnsupportedExpected());
963  BEAST_EXPECT(table->needValidatedLedger(1));
964 
965  std::set<uint256> enabled;
968  unsupported_.end(),
969  [&enabled](auto const& s) { enabled.insert(amendmentId(s)); });
970 
971  majorityAmendments_t majority;
972  table->doValidatedLedger(1, enabled, majority);
973  BEAST_EXPECT(table->hasUnsupportedEnabled());
974  BEAST_EXPECT(!table->firstUnsupportedExpected());
975 
976  NetClock::duration t{1000s};
980  [&majority, &t](auto const& s) {
981  majority[amendmentId(s)] = NetClock::time_point{--t};
982  });
983 
984  table->doValidatedLedger(1, enabled, majority);
985  BEAST_EXPECT(table->hasUnsupportedEnabled());
986  BEAST_EXPECT(
987  table->firstUnsupportedExpected() &&
988  *table->firstUnsupportedExpected() == NetClock::time_point{t} + w);
989 
990  // Make sure the table knows when it needs an update.
991  BEAST_EXPECT(!table->needValidatedLedger(256));
992  BEAST_EXPECT(table->needValidatedLedger(257));
993  }
994 
995  void
996  testFeature(uint256 const& feat)
997  {
998  testNoOnUnknown(feat);
999  testNoOnVetoed(feat);
1000  testVoteEnable(feat);
1001  testDetectMajority(feat);
1002  testLostMajority(feat);
1003  }
1004 
1005  void
1006  run() override
1007  {
1008  testConstruct();
1009  testGet();
1010  testBadConfig();
1011  testEnableVeto();
1013  testFeature({});
1015  }
1016 };
1017 
1018 BEAST_DEFINE_TESTSUITE(AmendmentTable, app, ripple);
1019 
1020 } // namespace ripple
ripple::tfGotMajority
constexpr std::uint32_t tfGotMajority
Definition: TxFlags.h:118
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
ripple::Application
Definition: Application.h:115
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:119
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::testVoteEnable
void testVoteEnable(uint256 const &feat)
Definition: AmendmentTable_test.cpp:730
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::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::makeValidators
std::vector< std::pair< PublicKey, SecretKey > > makeValidators(int num)
Definition: AmendmentTable_test.cpp:485
ripple::AmendmentTable_test::makeSection
static Section makeSection(uint256 const &amendment)
Definition: AmendmentTable_test.cpp:71
ripple::sfLedgerSequence
const SF_UINT32 sfLedgerSequence
std::pair
std::vector::reserve
T reserve(T... args)
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::QualityDirection::in
@ in
ripple::AmendmentTable_test::testNoOnUnknown
void testNoOnUnknown(uint256 const &feat)
Definition: AmendmentTable_test.cpp:603
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
ripple::AmendmentTable_test::testHasUnsupported
void testHasUnsupported()
Definition: AmendmentTable_test.cpp:953
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::base_uint::data
pointer data()
Definition: base_uint.h:122
ripple::AmendmentTable_test::weekTime
static NetClock::time_point weekTime(weeks w)
Definition: AmendmentTable_test.cpp:497
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::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:41
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::yes_
const std::vector< std::string > yes_
Definition: AmendmentTable_test.cpp:172
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::AmendmentTable_test::testDetectMajority
void testDetectMajority(uint256 const &feat)
Definition: AmendmentTable_test.cpp:809
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::doRound
void doRound(uint256 const &feat, AmendmentTable &table, weeks week, 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:504
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
std::set::count
T count(T... args)
ripple::STVector256
Definition: STVector256.h:29
ripple::AmendmentTable_test::makeSection
static Section makeSection(std::string const &name, std::vector< std::string > const &amendments)
Definition: AmendmentTable_test.cpp:54
std::map::empty
T empty(T... args)
ripple::Rules
Rules controlling protocol behavior.
Definition: Rules.h:33
ripple::AmendmentTable_test::testFeature
void testFeature(uint256 const &feat)
Definition: AmendmentTable_test.cpp:996
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:812
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
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::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:1006
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::AmendmentTable_test::testNoOnVetoed
void testNoOnVetoed(uint256 const &feat)
Definition: AmendmentTable_test.cpp:665
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
std::exception::what
T what(T... args)
ripple::AmendmentTable_test::testLostMajority
void testLostMajority(uint256 const &feat)
Definition: AmendmentTable_test.cpp:878
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
ripple::VoteBehavior::DefaultYes
@ DefaultYes