rippled
AmendmentTable.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/main/Application.h>
21 #include <ripple/app/misc/AmendmentTable.h>
22 #include <ripple/core/ConfigSections.h>
23 #include <ripple/core/DatabaseCon.h>
24 #include <ripple/protocol/STValidation.h>
25 #include <ripple/protocol/TxFlags.h>
26 #include <ripple/protocol/jss.h>
27 #include <boost/format.hpp>
28 #include <boost/regex.hpp>
29 #include <algorithm>
30 #include <mutex>
31 
32 namespace ripple {
33 
35 parseSection(Section const& section)
36 {
37  static boost::regex const re1(
38  "^" // start of line
39  "(?:\\s*)" // whitespace (optional)
40  "([abcdefABCDEF0-9]{64})" // <hexadecimal amendment ID>
41  "(?:\\s+)" // whitespace
42  "(\\S+)" // <description>
43  ,
44  boost::regex_constants::optimize);
45 
47 
48  for (auto const& line : section.lines())
49  {
50  boost::smatch match;
51 
52  if (!boost::regex_match(line, match, re1))
53  Throw<std::runtime_error>(
54  "Invalid entry '" + line + "' in [" + section.name() + "]");
55 
56  uint256 id;
57 
58  if (!id.SetHexExact(match[1]))
59  Throw<std::runtime_error>(
60  "Invalid amendment ID '" + match[1] + "' in [" +
61  section.name() + "]");
62 
63  names.push_back(std::make_pair(id, match[2]));
64  }
65 
66  return names;
67 }
68 
74 {
76  bool vetoed = false;
77 
83  bool enabled = false;
84 
86  bool supported = false;
87 
90 
91  explicit AmendmentState() = default;
92 };
93 
96 {
97 private:
98  // How many yes votes each amendment received
100 
101 public:
102  // number of trusted validations
104 
105  // number of votes needed
106  int mThreshold = 0;
107 
108  AmendmentSet() = default;
109 
110  void
111  tally(std::set<uint256> const& amendments)
112  {
114 
115  for (auto const& amendment : amendments)
116  ++votes_[amendment];
117  }
118 
119  int
120  votes(uint256 const& amendment) const
121  {
122  auto const& it = votes_.find(amendment);
123 
124  if (it == votes_.end())
125  return 0;
126 
127  return it->second;
128  }
129 };
130 
131 //------------------------------------------------------------------------------
132 
139 class AmendmentTableImpl final : public AmendmentTable
140 {
141 protected:
143 
146 
147  // Time that an amendment must hold a majority for
149 
150  // The amount of support that an amendment must receive
151  // 0 = 0% and 256 = 100%
152  int const majorityFraction_;
153 
154  // The results of the last voting round - may be empty if
155  // we haven't participated in one yet.
157 
158  // True if an unsupported amendment is enabled
160  // Unset if no unsupported amendments reach majority,
161  // else set to the earliest time an unsupported amendment
162  // will be enabled.
163  boost::optional<NetClock::time_point> firstUnsupportedExpected_;
164 
166 
167  // Finds or creates state
169  add(uint256 const& amendment);
170 
171  // Finds existing state
173  get(uint256 const& amendment);
174 
175  void
176  setJson(Json::Value& v, uint256 const& amendment, const AmendmentState&);
177 
178 public:
180  std::chrono::seconds majorityTime,
181  int majorityFraction,
182  Section const& supported,
183  Section const& enabled,
184  Section const& vetoed,
185  beast::Journal journal);
186 
187  uint256
188  find(std::string const& name) override;
189 
190  bool
191  veto(uint256 const& amendment) override;
192  bool
193  unVeto(uint256 const& amendment) override;
194 
195  bool
196  enable(uint256 const& amendment) override;
197  bool
198  disable(uint256 const& amendment) override;
199 
200  bool
201  isEnabled(uint256 const& amendment) override;
202  bool
203  isSupported(uint256 const& amendment) override;
204 
205  bool
206  hasUnsupportedEnabled() override;
207  boost::optional<NetClock::time_point>
208  firstUnsupportedExpected() override;
209 
211  getJson(int) override;
213  getJson(uint256 const&) override;
214 
215  bool
216  needValidatedLedger(LedgerIndex seq) override;
217 
218  void
220  LedgerIndex seq,
221  std::set<uint256> const& enabled,
222  majorityAmendments_t const& majority) override;
223 
225  doValidation(std::set<uint256> const& enabledAmendments) override;
226 
228  getDesired() override;
229 
231  doVoting(
232  NetClock::time_point closeTime,
233  std::set<uint256> const& enabledAmendments,
234  majorityAmendments_t const& majorityAmendments,
235  std::vector<std::shared_ptr<STValidation>> const& validations) override;
236 };
237 
238 //------------------------------------------------------------------------------
239 
241  std::chrono::seconds majorityTime,
242  int majorityFraction,
243  Section const& supported,
244  Section const& enabled,
245  Section const& vetoed,
246  beast::Journal journal)
247  : lastUpdateSeq_(0)
248  , majorityTime_(majorityTime)
249  , majorityFraction_(majorityFraction)
250  , unsupportedEnabled_(false)
251  , j_(journal)
252 {
253  assert(majorityFraction_ != 0);
254 
256 
257  for (auto const& a : parseSection(supported))
258  {
259  if (auto s = add(a.first))
260  {
261  JLOG(j_.debug()) << "Amendment " << a.first << " is supported.";
262 
263  if (!a.second.empty())
264  s->name = a.second;
265 
266  s->supported = true;
267  }
268  }
269 
270  for (auto const& a : parseSection(enabled))
271  {
272  if (auto s = add(a.first))
273  {
274  JLOG(j_.debug()) << "Amendment " << a.first << " is enabled.";
275 
276  if (!a.second.empty())
277  s->name = a.second;
278 
279  s->supported = true;
280  s->enabled = true;
281  }
282  }
283 
284  for (auto const& a : parseSection(vetoed))
285  {
286  // Unknown amendments are effectively vetoed already
287  if (auto s = get(a.first))
288  {
289  JLOG(j_.info()) << "Amendment " << a.first << " is vetoed.";
290 
291  if (!a.second.empty())
292  s->name = a.second;
293 
294  s->vetoed = true;
295  }
296  }
297 }
298 
300 AmendmentTableImpl::add(uint256 const& amendmentHash)
301 {
302  // call with the mutex held
303  return &amendmentMap_[amendmentHash];
304 }
305 
307 AmendmentTableImpl::get(uint256 const& amendmentHash)
308 {
309  // call with the mutex held
310  auto ret = amendmentMap_.find(amendmentHash);
311 
312  if (ret == amendmentMap_.end())
313  return nullptr;
314 
315  return &ret->second;
316 }
317 
318 uint256
320 {
322 
323  for (auto const& e : amendmentMap_)
324  {
325  if (name == e.second.name)
326  return e.first;
327  }
328 
329  return {};
330 }
331 
332 bool
334 {
336  auto s = add(amendment);
337 
338  if (s->vetoed)
339  return false;
340  s->vetoed = true;
341  return true;
342 }
343 
344 bool
346 {
348  auto s = get(amendment);
349 
350  if (!s || !s->vetoed)
351  return false;
352  s->vetoed = false;
353  return true;
354 }
355 
356 bool
358 {
360  auto s = add(amendment);
361 
362  if (s->enabled)
363  return false;
364 
365  s->enabled = true;
366 
367  if (!s->supported)
368  {
369  JLOG(j_.error()) << "Unsupported amendment " << amendment
370  << " activated.";
371  unsupportedEnabled_ = true;
372  }
373 
374  return true;
375 }
376 
377 bool
379 {
381  auto s = get(amendment);
382 
383  if (!s || !s->enabled)
384  return false;
385 
386  s->enabled = false;
387  return true;
388 }
389 
390 bool
392 {
394  auto s = get(amendment);
395  return s && s->enabled;
396 }
397 
398 bool
400 {
402  auto s = get(amendment);
403  return s && s->supported;
404 }
405 
406 bool
408 {
410  return unsupportedEnabled_;
411 }
412 
413 boost::optional<NetClock::time_point>
415 {
418 }
419 
422 {
423  // Get the list of amendments we support and do not
424  // veto, but that are not already enabled
425  std::vector<uint256> amendments;
426  amendments.reserve(amendmentMap_.size());
427 
428  {
430  for (auto const& e : amendmentMap_)
431  {
432  if (e.second.supported && !e.second.vetoed &&
433  (enabled.count(e.first) == 0))
434  {
435  amendments.push_back(e.first);
436  }
437  }
438  }
439 
440  if (!amendments.empty())
441  std::sort(amendments.begin(), amendments.end());
442 
443  return amendments;
444 }
445 
448 {
449  // Get the list of amendments we support and do not veto
450  return doValidation({});
451 }
452 
455  NetClock::time_point closeTime,
456  std::set<uint256> const& enabledAmendments,
457  majorityAmendments_t const& majorityAmendments,
459 {
460  JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count()
461  << ": " << enabledAmendments.size() << ", "
462  << majorityAmendments.size() << ", " << valSet.size();
463 
464  auto vote = std::make_unique<AmendmentSet>();
465 
466  // process validations for ledger before flag ledger
467  for (auto const& val : valSet)
468  {
469  if (val->isTrusted())
470  {
471  std::set<uint256> ballot;
472 
473  if (val->isFieldPresent(sfAmendments))
474  {
475  auto const choices = val->getFieldV256(sfAmendments);
476  ballot.insert(choices.begin(), choices.end());
477  }
478 
479  vote->tally(ballot);
480  }
481  }
482 
483  vote->mThreshold =
484  std::max(1, (vote->mTrustedValidations * majorityFraction_) / 256);
485 
486  JLOG(j_.debug()) << "Received " << vote->mTrustedValidations
487  << " trusted validations, threshold is: "
488  << vote->mThreshold;
489 
490  // Map of amendments to the action to be taken for each one. The action is
491  // the value of the flags in the pseudo-transaction
493 
494  {
496 
497  // process all amendments we know of
498  for (auto const& entry : amendmentMap_)
499  {
500  NetClock::time_point majorityTime = {};
501 
502  bool const hasValMajority =
503  (vote->votes(entry.first) >= vote->mThreshold);
504 
505  {
506  auto const it = majorityAmendments.find(entry.first);
507  if (it != majorityAmendments.end())
508  majorityTime = it->second;
509  }
510 
511  if (enabledAmendments.count(entry.first) != 0)
512  {
513  JLOG(j_.debug())
514  << entry.first << ": amendment already enabled";
515  }
516  else if (
517  hasValMajority && (majorityTime == NetClock::time_point{}) &&
518  !entry.second.vetoed)
519  {
520  // Ledger says no majority, validators say yes
521  JLOG(j_.debug()) << entry.first << ": amendment got majority";
522  actions[entry.first] = tfGotMajority;
523  }
524  else if (
525  !hasValMajority && (majorityTime != NetClock::time_point{}))
526  {
527  // Ledger says majority, validators say no
528  JLOG(j_.debug()) << entry.first << ": amendment lost majority";
529  actions[entry.first] = tfLostMajority;
530  }
531  else if (
532  (majorityTime != NetClock::time_point{}) &&
533  ((majorityTime + majorityTime_) <= closeTime) &&
534  !entry.second.vetoed)
535  {
536  // Ledger says majority held
537  JLOG(j_.debug()) << entry.first << ": amendment majority held";
538  actions[entry.first] = 0;
539  }
540  }
541 
542  // Stash for reporting
543  lastVote_ = std::move(vote);
544  }
545 
546  return actions;
547 }
548 
549 bool
551 {
553 
554  // Is there a ledger in which an amendment could have been enabled
555  // between these two ledger sequences?
556 
557  return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
558 }
559 
560 void
562  LedgerIndex ledgerSeq,
563  std::set<uint256> const& enabled,
564  majorityAmendments_t const& majority)
565 {
566  for (auto& e : enabled)
567  enable(e);
568 
570  // Since we have the whole list in `majority`, reset the time flag, even if
571  // it's currently set. If it's not set when the loop is done, then any
572  // prior unknown amendments have lost majority.
574  for (auto const& [hash, time] : majority)
575  {
576  auto s = add(hash);
577 
578  if (s->enabled)
579  continue;
580 
581  if (!s->supported)
582  {
583  JLOG(j_.info()) << "Unsupported amendment " << hash
584  << " reached majority at " << to_string(time);
587  }
588  }
591 }
592 
593 void
595  Json::Value& v,
596  const uint256& id,
597  const AmendmentState& fs)
598 {
599  if (!fs.name.empty())
600  v[jss::name] = fs.name;
601 
602  v[jss::supported] = fs.supported;
603  v[jss::vetoed] = fs.vetoed;
604  v[jss::enabled] = fs.enabled;
605 
606  if (!fs.enabled && lastVote_)
607  {
608  auto const votesTotal = lastVote_->mTrustedValidations;
609  auto const votesNeeded = lastVote_->mThreshold;
610  auto const votesFor = lastVote_->votes(id);
611 
612  v[jss::count] = votesFor;
613  v[jss::validations] = votesTotal;
614 
615  if (votesNeeded)
616  {
617  v[jss::vote] = votesFor * 256 / votesNeeded;
618  v[jss::threshold] = votesNeeded;
619  }
620  }
621 }
622 
625 {
627  {
629  for (auto const& e : amendmentMap_)
630  {
631  setJson(
632  ret[to_string(e.first)] = Json::objectValue, e.first, e.second);
633  }
634  }
635  return ret;
636 }
637 
640 {
642  Json::Value& jAmendment = (ret[to_string(amendmentID)] = Json::objectValue);
643 
644  {
646  auto a = add(amendmentID);
647  setJson(jAmendment, amendmentID, *a);
648  }
649 
650  return ret;
651 }
652 
655  std::chrono::seconds majorityTime,
656  int majorityFraction,
657  Section const& supported,
658  Section const& enabled,
659  Section const& vetoed,
660  beast::Journal journal)
661 {
662  return std::make_unique<AmendmentTableImpl>(
663  majorityTime, majorityFraction, supported, enabled, vetoed, journal);
664 }
665 
666 } // namespace ripple
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:43
ripple::AmendmentTableImpl::doVoting
std::map< uint256, std::uint32_t > doVoting(NetClock::time_point closeTime, std::set< uint256 > const &enabledAmendments, majorityAmendments_t const &majorityAmendments, std::vector< std::shared_ptr< STValidation >> const &validations) override
Definition: AmendmentTable.cpp:454
ripple::AmendmentState::supported
bool supported
Indicates an amendment that this server has code support for.
Definition: AmendmentTable.cpp:86
ripple::AmendmentTableImpl::lastUpdateSeq_
std::uint32_t lastUpdateSeq_
Definition: AmendmentTable.cpp:145
ripple::AmendmentTableImpl::disable
bool disable(uint256 const &amendment) override
Definition: AmendmentTable.cpp:378
ripple::AmendmentTableImpl::hasUnsupportedEnabled
bool hasUnsupportedEnabled() override
returns true if one or more amendments on the network have been enabled that this server does not sup...
Definition: AmendmentTable.cpp:407
ripple::AmendmentState
Current state of an amendment.
Definition: AmendmentTable.cpp:73
ripple::AmendmentTableImpl::firstUnsupportedExpected_
boost::optional< NetClock::time_point > firstUnsupportedExpected_
Definition: AmendmentTable.cpp:163
std::string
STL class.
std::shared_ptr
STL class.
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::AmendmentSet::AmendmentSet
AmendmentSet()=default
ripple::AmendmentTableImpl::mutex_
std::mutex mutex_
Definition: AmendmentTable.cpp:142
ripple::AmendmentTableImpl::unsupportedEnabled_
bool unsupportedEnabled_
Definition: AmendmentTable.cpp:159
std::vector
STL class.
std::map::find
T find(T... args)
ripple::make_AmendmentTable
std::unique_ptr< AmendmentTable > make_AmendmentTable(std::chrono::seconds majorityTime, int majorityFraction, Section const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
Definition: AmendmentTable.cpp:654
std::set::size
T size(T... args)
ripple::AmendmentTableImpl::isSupported
bool isSupported(uint256 const &amendment) override
Definition: AmendmentTable.cpp:399
std::chrono::seconds
ripple::AmendmentState::vetoed
bool vetoed
If an amendment is vetoed, a server will not support it.
Definition: AmendmentTable.cpp:76
std::lock_guard
STL class.
ripple::AmendmentTableImpl::unVeto
bool unVeto(uint256 const &amendment) override
Definition: AmendmentTable.cpp:345
ripple::AmendmentSet::tally
void tally(std::set< uint256 > const &amendments)
Definition: AmendmentTable.cpp:111
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
ripple::AmendmentTableImpl::isEnabled
bool isEnabled(uint256 const &amendment) override
Definition: AmendmentTable.cpp:391
ripple::tfLostMajority
const std::uint32_t tfLostMajority
Definition: TxFlags.h:102
ripple::tfGotMajority
const std::uint32_t tfGotMajority
Definition: TxFlags.h:101
ripple::AmendmentTableImpl
Track the list of "amendments".
Definition: AmendmentTable.cpp:139
std::sort
T sort(T... args)
algorithm
ripple::parseSection
static std::vector< std::pair< uint256, std::string > > parseSection(Section const &section)
Definition: AmendmentTable.cpp:35
ripple::AmendmentSet::votes_
hash_map< uint256, int > votes_
Definition: AmendmentTable.cpp:99
std::vector::push_back
T push_back(T... args)
ripple::base_uint< 256 >
ripple::Section::name
std::string const & name() const
Returns the name of this section.
Definition: BasicConfig.h:58
std::chrono::time_point::time_since_epoch
T time_since_epoch(T... args)
ripple::AmendmentTableImpl::setJson
void setJson(Json::Value &v, uint256 const &amendment, const AmendmentState &)
Definition: AmendmentTable.cpp:594
ripple::AmendmentTableImpl::majorityFraction_
const int majorityFraction_
Definition: AmendmentTable.cpp:152
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::AmendmentTableImpl::firstUnsupportedExpected
boost::optional< NetClock::time_point > firstUnsupportedExpected() override
Definition: AmendmentTable.cpp:414
ripple::AmendmentTableImpl::find
uint256 find(std::string const &name) override
Definition: AmendmentTable.cpp:319
ripple::AmendmentState::AmendmentState
AmendmentState()=default
ripple::AmendmentSet::votes
int votes(uint256 const &amendment) const
Definition: AmendmentTable.cpp:120
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
std::chrono::time_point
ripple::Section::lines
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition: BasicConfig.h:67
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
std::map
STL class.
ripple::AmendmentTableImpl::AmendmentTableImpl
AmendmentTableImpl(std::chrono::seconds majorityTime, int majorityFraction, Section const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
Definition: AmendmentTable.cpp:240
ripple::AmendmentTableImpl::veto
bool veto(uint256 const &amendment) override
Definition: AmendmentTable.cpp:333
ripple::AmendmentSet::mTrustedValidations
int mTrustedValidations
Definition: AmendmentTable.cpp:103
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::AmendmentTableImpl::getJson
Json::Value getJson(int) override
Definition: AmendmentTable.cpp:624
ripple::AmendmentTableImpl::amendmentMap_
hash_map< uint256, AmendmentState > amendmentMap_
Definition: AmendmentTable.cpp:144
ripple::AmendmentTableImpl::doValidation
std::vector< uint256 > doValidation(std::set< uint256 > const &enabledAmendments) override
Definition: AmendmentTable.cpp:421
ripple::AmendmentTableImpl::lastVote_
std::unique_ptr< AmendmentSet > lastVote_
Definition: AmendmentTable.cpp:156
std::set::insert
T insert(T... args)
ripple::AmendmentTableImpl::majorityTime_
const std::chrono::seconds majorityTime_
Definition: AmendmentTable.cpp:148
ripple::AmendmentState::enabled
bool enabled
Indicates that the amendment has been enabled.
Definition: AmendmentTable.cpp:83
ripple::AmendmentSet::mThreshold
int mThreshold
Definition: AmendmentTable.cpp:106
ripple::AmendmentState::name
std::string name
The name of this amendment, possibly empty.
Definition: AmendmentTable.cpp:89
std::set::count
T count(T... args)
std::string::empty
T empty(T... args)
ripple::AmendmentTableImpl::needValidatedLedger
bool needValidatedLedger(LedgerIndex seq) override
Called to determine whether the amendment logic needs to process a new validated ledger.
Definition: AmendmentTable.cpp:550
mutex
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::make_pair
T make_pair(T... args)
std::map::end
T end(T... args)
ripple::sfAmendments
const SF_Vec256 sfAmendments(access, STI_VECTOR256, 3, "Amendments")
Definition: SField.h:491
std::max
T max(T... args)
ripple::AmendmentTableImpl::j_
const beast::Journal j_
Definition: AmendmentTable.cpp:165
ripple::AmendmentSet
The status of all amendments requested in a given window.
Definition: AmendmentTable.cpp:95
ripple::AmendmentTable
The amendment table stores the list of enabled and potential amendments.
Definition: AmendmentTable.h:34
std::unique_ptr
STL class.
ripple::AmendmentTableImpl::get
AmendmentState * get(uint256 const &amendment)
Definition: AmendmentTable.cpp:307
std::unordered_map
STL class.
std::set
STL class.
ripple::AmendmentTableImpl::add
AmendmentState * add(uint256 const &amendment)
Definition: AmendmentTable.cpp:300
ripple::AmendmentTableImpl::getDesired
std::vector< uint256 > getDesired() override
Definition: AmendmentTable.cpp:447
ripple::AmendmentTableImpl::doValidatedLedger
void doValidatedLedger(LedgerIndex seq, std::set< uint256 > const &enabled, majorityAmendments_t const &majority) override
Definition: AmendmentTable.cpp:561
ripple::AmendmentTableImpl::enable
bool enable(uint256 const &amendment) override
Definition: AmendmentTable.cpp:357
Json::Value
Represents a JSON value.
Definition: json_value.h:145