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 
161  // Unset if no unsupported amendments reach majority,
162  // else set to the earliest time an unsupported amendment
163  // will be enabled.
164  boost::optional<NetClock::time_point> firstUnsupportedExpected_;
165 
167 
168  // Finds or creates state. Must be called with mutex_ locked.
170  add(uint256 const& amendment, std::lock_guard<std::mutex> const& sl);
171 
172  // Finds existing state. Must be called with mutex_ locked.
174  get(uint256 const& amendment, std::lock_guard<std::mutex> const& sl);
175 
176  AmendmentState const*
177  get(uint256 const& amendment, std::lock_guard<std::mutex> const& sl) const;
178 
179  // Injects amendment json into v. Must be called with mutex_ locked.
180  void
181  injectJson(
182  Json::Value& v,
183  uint256 const& amendment,
184  AmendmentState const& state,
185  std::lock_guard<std::mutex> const& sl) const;
186 
187 public:
189  std::chrono::seconds majorityTime,
190  int majorityFraction,
191  Section const& supported,
192  Section const& enabled,
193  Section const& vetoed,
194  beast::Journal journal);
195 
196  uint256
197  find(std::string const& name) const override;
198 
199  bool
200  veto(uint256 const& amendment) override;
201  bool
202  unVeto(uint256 const& amendment) override;
203 
204  bool
205  enable(uint256 const& amendment) override;
206 
207  bool
208  isEnabled(uint256 const& amendment) const override;
209  bool
210  isSupported(uint256 const& amendment) const override;
211 
212  bool
213  hasUnsupportedEnabled() const override;
214 
215  boost::optional<NetClock::time_point>
216  firstUnsupportedExpected() const override;
217 
219  getJson() const override;
221  getJson(uint256 const&) const override;
222 
223  bool
224  needValidatedLedger(LedgerIndex seq) const override;
225 
226  void
228  LedgerIndex seq,
229  std::set<uint256> const& enabled,
230  majorityAmendments_t const& majority) override;
231 
233  doValidation(std::set<uint256> const& enabledAmendments) const override;
234 
236  getDesired() const override;
237 
239  doVoting(
240  NetClock::time_point closeTime,
241  std::set<uint256> const& enabledAmendments,
242  majorityAmendments_t const& majorityAmendments,
243  std::vector<std::shared_ptr<STValidation>> const& validations) override;
244 };
245 
246 //------------------------------------------------------------------------------
247 
249  std::chrono::seconds majorityTime,
250  int majorityFraction,
251  Section const& supported,
252  Section const& enabled,
253  Section const& vetoed,
254  beast::Journal journal)
255  : lastUpdateSeq_(0)
256  , majorityTime_(majorityTime)
257  , majorityFraction_(majorityFraction)
258  , unsupportedEnabled_(false)
259  , j_(journal)
260 {
261  assert(majorityFraction_ != 0);
262 
264 
265  for (auto const& a : parseSection(supported))
266  {
267  if (auto s = add(a.first, sl))
268  {
269  JLOG(j_.debug()) << "Amendment " << a.first << " is supported.";
270 
271  if (!a.second.empty())
272  s->name = a.second;
273 
274  s->supported = true;
275  }
276  }
277 
278  for (auto const& a : parseSection(enabled))
279  {
280  if (auto s = add(a.first, sl))
281  {
282  JLOG(j_.debug()) << "Amendment " << a.first << " is enabled.";
283 
284  if (!a.second.empty())
285  s->name = a.second;
286 
287  s->supported = true;
288  s->enabled = true;
289  }
290  }
291 
292  for (auto const& a : parseSection(vetoed))
293  {
294  // Unknown amendments are effectively vetoed already
295  if (auto s = get(a.first, sl))
296  {
297  JLOG(j_.info()) << "Amendment " << a.first << " is vetoed.";
298 
299  if (!a.second.empty())
300  s->name = a.second;
301 
302  s->vetoed = true;
303  }
304  }
305 }
306 
309  uint256 const& amendmentHash,
311 {
312  // call with the mutex held
313  return &amendmentMap_[amendmentHash];
314 }
315 
318  uint256 const& amendmentHash,
319  std::lock_guard<std::mutex> const& sl)
320 {
321  // Forward to the const version of get.
322  return const_cast<AmendmentState*>(
323  std::as_const(*this).get(amendmentHash, sl));
324 }
325 
326 AmendmentState const*
328  uint256 const& amendmentHash,
329  std::lock_guard<std::mutex> const&) const
330 {
331  // call with the mutex held
332  auto ret = amendmentMap_.find(amendmentHash);
333 
334  if (ret == amendmentMap_.end())
335  return nullptr;
336 
337  return &ret->second;
338 }
339 
340 uint256
342 {
344 
345  for (auto const& e : amendmentMap_)
346  {
347  if (name == e.second.name)
348  return e.first;
349  }
350 
351  return {};
352 }
353 
354 bool
356 {
358  auto s = add(amendment, sl);
359 
360  if (s->vetoed)
361  return false;
362  s->vetoed = true;
363  return true;
364 }
365 
366 bool
368 {
370  auto s = get(amendment, sl);
371 
372  if (!s || !s->vetoed)
373  return false;
374  s->vetoed = false;
375  return true;
376 }
377 
378 bool
380 {
382  auto s = add(amendment, sl);
383 
384  if (s->enabled)
385  return false;
386 
387  s->enabled = true;
388 
389  if (!s->supported)
390  {
391  JLOG(j_.error()) << "Unsupported amendment " << amendment
392  << " activated.";
393  unsupportedEnabled_ = true;
394  }
395 
396  return true;
397 }
398 
399 bool
400 AmendmentTableImpl::isEnabled(uint256 const& amendment) const
401 {
403  auto s = get(amendment, sl);
404  return s && s->enabled;
405 }
406 
407 bool
409 {
411  auto s = get(amendment, sl);
412  return s && s->supported;
413 }
414 
415 bool
417 {
419  return unsupportedEnabled_;
420 }
421 
422 boost::optional<NetClock::time_point>
424 {
427 }
428 
431 {
432  // Get the list of amendments we support and do not
433  // veto, but that are not already enabled
434  std::vector<uint256> amendments;
435 
436  {
438  amendments.reserve(amendmentMap_.size());
439  for (auto const& e : amendmentMap_)
440  {
441  if (e.second.supported && !e.second.vetoed &&
442  (enabled.count(e.first) == 0))
443  {
444  amendments.push_back(e.first);
445  }
446  }
447  }
448 
449  if (!amendments.empty())
450  std::sort(amendments.begin(), amendments.end());
451 
452  return amendments;
453 }
454 
457 {
458  // Get the list of amendments we support and do not veto
459  return doValidation({});
460 }
461 
464  NetClock::time_point closeTime,
465  std::set<uint256> const& enabledAmendments,
466  majorityAmendments_t const& majorityAmendments,
468 {
469  JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count()
470  << ": " << enabledAmendments.size() << ", "
471  << majorityAmendments.size() << ", " << valSet.size();
472 
473  auto vote = std::make_unique<AmendmentSet>();
474 
475  // process validations for ledger before flag ledger
476  for (auto const& val : valSet)
477  {
478  if (val->isTrusted())
479  {
480  std::set<uint256> ballot;
481 
482  if (val->isFieldPresent(sfAmendments))
483  {
484  auto const choices = val->getFieldV256(sfAmendments);
485  ballot.insert(choices.begin(), choices.end());
486  }
487 
488  vote->tally(ballot);
489  }
490  }
491 
492  vote->mThreshold =
493  std::max(1, (vote->mTrustedValidations * majorityFraction_) / 256);
494 
495  JLOG(j_.debug()) << "Received " << vote->mTrustedValidations
496  << " trusted validations, threshold is: "
497  << vote->mThreshold;
498 
499  // Map of amendments to the action to be taken for each one. The action is
500  // the value of the flags in the pseudo-transaction
502 
504 
505  // process all amendments we know of
506  for (auto const& entry : amendmentMap_)
507  {
508  NetClock::time_point majorityTime = {};
509 
510  bool const hasValMajority =
511  (vote->votes(entry.first) >= vote->mThreshold);
512 
513  {
514  auto const it = majorityAmendments.find(entry.first);
515  if (it != majorityAmendments.end())
516  majorityTime = it->second;
517  }
518 
519  if (enabledAmendments.count(entry.first) != 0)
520  {
521  JLOG(j_.debug()) << entry.first << ": amendment already enabled";
522  }
523  else if (
524  hasValMajority && (majorityTime == NetClock::time_point{}) &&
525  !entry.second.vetoed)
526  {
527  // Ledger says no majority, validators say yes
528  JLOG(j_.debug()) << entry.first << ": amendment got majority";
529  actions[entry.first] = tfGotMajority;
530  }
531  else if (!hasValMajority && (majorityTime != NetClock::time_point{}))
532  {
533  // Ledger says majority, validators say no
534  JLOG(j_.debug()) << entry.first << ": amendment lost majority";
535  actions[entry.first] = tfLostMajority;
536  }
537  else if (
538  (majorityTime != NetClock::time_point{}) &&
539  ((majorityTime + majorityTime_) <= closeTime) &&
540  !entry.second.vetoed)
541  {
542  // Ledger says majority held
543  JLOG(j_.debug()) << entry.first << ": amendment majority held";
544  actions[entry.first] = 0;
545  }
546  }
547 
548  // Stash for reporting
549  lastVote_ = std::move(vote);
550  return actions;
551 }
552 
553 bool
555 {
557 
558  // Is there a ledger in which an amendment could have been enabled
559  // between these two ledger sequences?
560 
561  return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
562 }
563 
564 void
566  LedgerIndex ledgerSeq,
567  std::set<uint256> const& enabled,
568  majorityAmendments_t const& majority)
569 {
570  for (auto& e : enabled)
571  enable(e);
572 
574 
575  // Remember the ledger sequence of this update.
576  lastUpdateSeq_ = ledgerSeq;
577 
578  // Since we have the whole list in `majority`, reset the time flag, even
579  // if it's currently set. If it's not set when the loop is done, then any
580  // prior unknown amendments have lost majority.
582  for (auto const& [hash, time] : majority)
583  {
584  auto s = add(hash, sl);
585 
586  if (s->enabled)
587  continue;
588 
589  if (!s->supported)
590  {
591  JLOG(j_.info()) << "Unsupported amendment " << hash
592  << " reached majority at " << to_string(time);
595  }
596  }
599 }
600 
601 void
603  Json::Value& v,
604  const uint256& id,
605  const AmendmentState& fs,
606  std::lock_guard<std::mutex> const&) const
607 {
608  if (!fs.name.empty())
609  v[jss::name] = fs.name;
610 
611  v[jss::supported] = fs.supported;
612  v[jss::vetoed] = fs.vetoed;
613  v[jss::enabled] = fs.enabled;
614 
615  if (!fs.enabled && lastVote_)
616  {
617  auto const votesTotal = lastVote_->mTrustedValidations;
618  auto const votesNeeded = lastVote_->mThreshold;
619  auto const votesFor = lastVote_->votes(id);
620 
621  v[jss::count] = votesFor;
622  v[jss::validations] = votesTotal;
623 
624  if (votesNeeded)
625  {
626  v[jss::vote] = votesFor * 256 / votesNeeded;
627  v[jss::threshold] = votesNeeded;
628  }
629  }
630 }
631 
634 {
636  {
638  for (auto const& e : amendmentMap_)
639  {
640  injectJson(
641  ret[to_string(e.first)] = Json::objectValue,
642  e.first,
643  e.second,
644  sl);
645  }
646  }
647  return ret;
648 }
649 
651 AmendmentTableImpl::getJson(uint256 const& amendmentID) const
652 {
654  Json::Value& jAmendment = (ret[to_string(amendmentID)] = Json::objectValue);
655 
656  {
658  auto a = get(amendmentID, sl);
659  if (a)
660  injectJson(jAmendment, amendmentID, *a, sl);
661  }
662 
663  return ret;
664 }
665 
668  std::chrono::seconds majorityTime,
669  int majorityFraction,
670  Section const& supported,
671  Section const& enabled,
672  Section const& vetoed,
673  beast::Journal journal)
674 {
675  return std::make_unique<AmendmentTableImpl>(
676  majorityTime, majorityFraction, supported, enabled, vetoed, journal);
677 }
678 
679 } // 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:463
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::AmendmentState
Current state of an amendment.
Definition: AmendmentTable.cpp:73
ripple::AmendmentTableImpl::firstUnsupportedExpected_
boost::optional< NetClock::time_point > firstUnsupportedExpected_
Definition: AmendmentTable.cpp:164
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::getDesired
std::vector< uint256 > getDesired() const override
Definition: AmendmentTable.cpp:456
ripple::AmendmentTableImpl::mutex_
std::mutex mutex_
Definition: AmendmentTable.cpp:142
ripple::AmendmentTableImpl::unsupportedEnabled_
bool unsupportedEnabled_
Definition: AmendmentTable.cpp:159
ripple::AmendmentTableImpl::doValidation
std::vector< uint256 > doValidation(std::set< uint256 > const &enabledAmendments) const override
Definition: AmendmentTable.cpp:430
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:667
std::set::size
T size(T... args)
std::chrono::seconds
ripple::AmendmentTableImpl::find
uint256 find(std::string const &name) const override
Definition: AmendmentTable.cpp:341
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:367
ripple::AmendmentTableImpl::firstUnsupportedExpected
boost::optional< NetClock::time_point > firstUnsupportedExpected() const override
Definition: AmendmentTable.cpp:423
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::tfLostMajority
const std::uint32_t tfLostMajority
Definition: TxFlags.h:102
std::as_const
T as_const(T... args)
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::majorityFraction_
const int majorityFraction_
Definition: AmendmentTable.cpp:152
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::AmendmentState::AmendmentState
AmendmentState()=default
ripple::AmendmentSet::votes
int votes(uint256 const &amendment) const
Definition: AmendmentTable.cpp:120
ripple::AmendmentTableImpl::isSupported
bool isSupported(uint256 const &amendment) const override
Definition: AmendmentTable.cpp:408
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:248
ripple::AmendmentTableImpl::veto
bool veto(uint256 const &amendment) override
Definition: AmendmentTable.cpp:355
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::amendmentMap_
hash_map< uint256, AmendmentState > amendmentMap_
Definition: AmendmentTable.cpp:144
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::AmendmentTableImpl::getJson
Json::Value getJson() const override
Definition: AmendmentTable.cpp:633
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)
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
ripple::AmendmentTableImpl::isEnabled
bool isEnabled(uint256 const &amendment) const override
Definition: AmendmentTable.cpp:400
std::max
T max(T... args)
ripple::AmendmentTableImpl::injectJson
void injectJson(Json::Value &v, uint256 const &amendment, AmendmentState const &state, std::lock_guard< std::mutex > const &sl) const
Definition: AmendmentTable.cpp:602
ripple::AmendmentTableImpl::j_
const beast::Journal j_
Definition: AmendmentTable.cpp:166
ripple::AmendmentTableImpl::add
AmendmentState * add(uint256 const &amendment, std::lock_guard< std::mutex > const &sl)
Definition: AmendmentTable.cpp:308
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
ripple::AmendmentTableImpl::needValidatedLedger
bool needValidatedLedger(LedgerIndex seq) const override
Called to determine whether the amendment logic needs to process a new validated ledger.
Definition: AmendmentTable.cpp:554
std::unique_ptr
STL class.
std::unordered_map
STL class.
std::set
STL class.
ripple::AmendmentTableImpl::doValidatedLedger
void doValidatedLedger(LedgerIndex seq, std::set< uint256 > const &enabled, majorityAmendments_t const &majority) override
Definition: AmendmentTable.cpp:565
ripple::AmendmentTableImpl::enable
bool enable(uint256 const &amendment) override
Definition: AmendmentTable.cpp:379
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::AmendmentTableImpl::get
AmendmentState * get(uint256 const &amendment, std::lock_guard< std::mutex > const &sl)
Definition: AmendmentTable.cpp:317
ripple::AmendmentTableImpl::hasUnsupportedEnabled
bool hasUnsupportedEnabled() const override
returns true if one or more amendments on the network have been enabled that this server does not sup...
Definition: AmendmentTable.cpp:416