rippled
ValidatorList.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2015 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/HashRouter.h>
21 #include <ripple/app/misc/ValidatorList.h>
22 #include <ripple/basics/FileUtilities.h>
23 #include <ripple/basics/Slice.h>
24 #include <ripple/basics/StringUtilities.h>
25 #include <ripple/basics/base64.h>
26 #include <ripple/json/json_reader.h>
27 #include <ripple/overlay/Overlay.h>
28 #include <ripple/protocol/STValidation.h>
29 #include <ripple/protocol/jss.h>
30 #include <ripple/protocol/messages.h>
31 #include <boost/regex.hpp>
32 
33 #include <date/date.h>
34 
35 #include <cmath>
36 #include <mutex>
37 #include <shared_mutex>
38 
39 namespace ripple {
40 
43 {
44  switch (disposition)
45  {
47  return "accepted";
49  return "same_sequence";
51  return "unsupported_version";
53  return "untrusted";
55  return "stale";
57  return "invalid";
58  }
59  return "unknown";
60 }
61 
63 
65  ManifestCache& validatorManifests,
66  ManifestCache& publisherManifests,
67  TimeKeeper& timeKeeper,
68  std::string const& databasePath,
70  boost::optional<std::size_t> minimumQuorum)
71  : validatorManifests_(validatorManifests)
72  , publisherManifests_(publisherManifests)
73  , timeKeeper_(timeKeeper)
74  , dataPath_(databasePath)
75  , j_(j)
76  , quorum_(minimumQuorum.value_or(1)) // Genesis ledger quorum
77  , minimumQuorum_(minimumQuorum)
78 {
79 }
80 
81 bool
83  PublicKey const& localSigningKey,
84  std::vector<std::string> const& configKeys,
85  std::vector<std::string> const& publisherKeys)
86 {
87  static boost::regex const re(
88  "[[:space:]]*" // skip leading whitespace
89  "([[:alnum:]]+)" // node identity
90  "(?:" // begin optional comment block
91  "[[:space:]]+" // (skip all leading whitespace)
92  "(?:" // begin optional comment
93  "(.*[^[:space:]]+)" // the comment
94  "[[:space:]]*" // (skip all trailing whitespace)
95  ")?" // end optional comment
96  ")?" // end optional comment block
97  );
98 
100 
101  JLOG(j_.debug())
102  << "Loading configured trusted validator list publisher keys";
103 
104  std::size_t count = 0;
105  for (auto key : publisherKeys)
106  {
107  JLOG(j_.trace()) << "Processing '" << key << "'";
108 
109  auto const ret = strUnHex(key);
110 
111  if (!ret || !publicKeyType(makeSlice(*ret)))
112  {
113  JLOG(j_.error()) << "Invalid validator list publisher key: " << key;
114  return false;
115  }
116 
117  auto id = PublicKey(makeSlice(*ret));
118 
120  {
121  JLOG(j_.warn())
122  << "Configured validator list publisher key is revoked: "
123  << key;
124  continue;
125  }
126 
127  if (publisherLists_.count(id))
128  {
129  JLOG(j_.warn())
130  << "Duplicate validator list publisher key: " << key;
131  continue;
132  }
133 
134  publisherLists_[id].available = false;
135  ++count;
136  }
137 
138  JLOG(j_.debug()) << "Loaded " << count << " keys";
139 
140  localPubKey_ = validatorManifests_.getMasterKey(localSigningKey);
141 
142  // Treat local validator key as though it was listed in the config
143  if (localPubKey_.size())
144  keyListings_.insert({localPubKey_, 1});
145 
146  JLOG(j_.debug()) << "Loading configured validator keys";
147 
148  count = 0;
149  PublicKey local;
150  for (auto const& n : configKeys)
151  {
152  JLOG(j_.trace()) << "Processing '" << n << "'";
153 
154  boost::smatch match;
155 
156  if (!boost::regex_match(n, match, re))
157  {
158  JLOG(j_.error()) << "Malformed entry: '" << n << "'";
159  return false;
160  }
161 
162  auto const id = parseBase58<PublicKey>(TokenType::NodePublic, match[1]);
163 
164  if (!id)
165  {
166  JLOG(j_.error()) << "Invalid node identity: " << match[1];
167  return false;
168  }
169 
170  // Skip local key which was already added
171  if (*id == localPubKey_ || *id == localSigningKey)
172  continue;
173 
174  auto ret = keyListings_.insert({*id, 1});
175  if (!ret.second)
176  {
177  JLOG(j_.warn()) << "Duplicate node identity: " << match[1];
178  continue;
179  }
180  auto it = publisherLists_.emplace(
181  std::piecewise_construct,
182  std::forward_as_tuple(local),
184  // Config listed keys never expire
185  if (it.second)
186  it.first->second.expiration = TimeKeeper::time_point::max();
187  it.first->second.list.emplace_back(*id);
188  it.first->second.available = true;
189  ++count;
190  }
191 
192  JLOG(j_.debug()) << "Loaded " << count << " entries";
193 
194  return true;
195 }
196 
197 boost::filesystem::path
199 {
200  return dataPath_ / (filePrefix_ + strHex(pubKey));
201 }
202 
203 void
205  PublicKey const& pubKey,
206  PublisherList const& publisher)
207 {
208  if (dataPath_.empty())
209  return;
210 
211  boost::filesystem::path const filename = GetCacheFileName(pubKey);
212 
213  boost::system::error_code ec;
214 
216 
217  value["manifest"] = publisher.rawManifest;
218  value["blob"] = publisher.rawBlob;
219  value["signature"] = publisher.rawSignature;
220  value["version"] = publisher.rawVersion;
221 
222  writeFileContents(ec, filename, value.toStyledString());
223 
224  if (ec)
225  {
226  // Log and ignore any file I/O exceptions
227  JLOG(j_.error()) << "Problem writing " << filename << " " << ec.value()
228  << ": " << ec.message();
229  }
230 }
231 
234  std::string const& manifest,
235  std::string const& blob,
236  std::string const& signature,
237  std::uint32_t version,
238  std::string siteUri,
239  uint256 const& hash,
240  Overlay& overlay,
241  HashRouter& hashRouter)
242 {
243  auto const result =
244  applyList(manifest, blob, signature, version, std::move(siteUri), hash);
245  auto const disposition = result.disposition;
246 
247  bool broadcast = disposition == ListDisposition::accepted ||
248  disposition == ListDisposition::same_sequence;
249 
250  if (broadcast)
251  {
252  assert(result.available && result.publisherKey && result.sequence);
253  auto const toSkip = hashRouter.shouldRelay(hash);
254 
255  if (toSkip)
256  {
257  protocol::TMValidatorList msg;
258  msg.set_manifest(manifest);
259  msg.set_blob(blob);
260  msg.set_signature(signature);
261  msg.set_version(version);
262 
263  auto const& publisherKey = *result.publisherKey;
264  auto const sequence = *result.sequence;
265 
266  // Can't use overlay.foreach here because we need to modify
267  // the peer, and foreach provides a const&
268  auto message =
269  std::make_shared<Message>(msg, protocol::mtVALIDATORLIST);
270  for (auto& peer : overlay.getActivePeers())
271  {
272  if (toSkip->count(peer->id()) == 0 &&
273  peer->supportsFeature(
275  peer->publisherListSequence(publisherKey) < sequence)
276  {
277  peer->send(message);
278 
279  JLOG(j_.debug())
280  << "Sent validator list for " << strHex(publisherKey)
281  << " with sequence " << sequence << " to "
282  << peer->getRemoteAddress().to_string() << " ("
283  << peer->id() << ")";
284  // Don't send it next time.
285  hashRouter.addSuppressionPeer(hash, peer->id());
286  peer->setPublisherListSequence(publisherKey, sequence);
287  }
288  }
289  }
290  }
291 
292  return result;
293 }
294 
297  std::string const& manifest,
298  std::string const& blob,
299  std::string const& signature,
300  std::uint32_t version,
301  std::string siteUri,
302  boost::optional<uint256> const& hash)
303 {
304  using namespace std::string_literals;
305 
306  if (version != requiredListVersion)
308 
310 
311  Json::Value list;
312  PublicKey pubKey;
313  auto const result = verify(list, pubKey, manifest, blob, signature);
314  if (result != ListDisposition::accepted)
315  {
316  if (result == ListDisposition::same_sequence &&
317  publisherLists_.count(pubKey))
318  {
319  // We've seen this valid list already, so return
320  // what we know about it.
321  auto const& publisher = publisherLists_[pubKey];
322  return PublisherListStats{
323  result, pubKey, publisher.available, publisher.sequence};
324  }
325  return PublisherListStats{result};
326  }
327 
328  // Update publisher's list
329  Json::Value const& newList = list["validators"];
330  auto& publisher = publisherLists_[pubKey];
331  publisher.available = true;
332  publisher.sequence = list["sequence"].asUInt();
333  publisher.expiration = TimeKeeper::time_point{
334  TimeKeeper::duration{list["expiration"].asUInt()}};
335  publisher.siteUri = std::move(siteUri);
336  publisher.rawManifest = manifest;
337  publisher.rawBlob = blob;
338  publisher.rawSignature = signature;
339  publisher.rawVersion = version;
340  if (hash)
341  publisher.hash = *hash;
342  std::vector<PublicKey>& publisherList = publisher.list;
343 
344  PublisherListStats const applyResult{
345  result, pubKey, publisher.available, publisher.sequence};
346 
347  std::vector<PublicKey> oldList = publisherList;
348  publisherList.clear();
349  publisherList.reserve(newList.size());
350  std::vector<std::string> manifests;
351  for (auto const& val : newList)
352  {
353  if (val.isObject() && val.isMember("validation_public_key") &&
354  val["validation_public_key"].isString())
355  {
356  boost::optional<Blob> const ret =
357  strUnHex(val["validation_public_key"].asString());
358 
359  if (!ret || !publicKeyType(makeSlice(*ret)))
360  {
361  JLOG(j_.error()) << "Invalid node identity: "
362  << val["validation_public_key"].asString();
363  }
364  else
365  {
366  publisherList.push_back(
367  PublicKey(Slice{ret->data(), ret->size()}));
368  }
369 
370  if (val.isMember("manifest") && val["manifest"].isString())
371  manifests.push_back(val["manifest"].asString());
372  }
373  }
374 
375  // Update keyListings_ for added and removed keys
376  std::sort(publisherList.begin(), publisherList.end());
377 
378  auto iNew = publisherList.begin();
379  auto iOld = oldList.begin();
380  while (iNew != publisherList.end() || iOld != oldList.end())
381  {
382  if (iOld == oldList.end() ||
383  (iNew != publisherList.end() && *iNew < *iOld))
384  {
385  // Increment list count for added keys
386  ++keyListings_[*iNew];
387  ++iNew;
388  }
389  else if (
390  iNew == publisherList.end() ||
391  (iOld != oldList.end() && *iOld < *iNew))
392  {
393  // Decrement list count for removed keys
394  if (keyListings_[*iOld] <= 1)
395  keyListings_.erase(*iOld);
396  else
397  --keyListings_[*iOld];
398  ++iOld;
399  }
400  else
401  {
402  ++iNew;
403  ++iOld;
404  }
405  }
406 
407  if (publisherList.empty())
408  {
409  JLOG(j_.warn()) << "No validator keys included in valid list";
410  }
411 
412  for (auto const& valManifest : manifests)
413  {
414  auto m = deserializeManifest(base64_decode(valManifest));
415 
416  if (!m || !keyListings_.count(m->masterKey))
417  {
418  JLOG(j_.warn()) << "List for " << strHex(pubKey)
419  << " contained untrusted validator manifest";
420  continue;
421  }
422 
423  if (auto const r = validatorManifests_.applyManifest(std::move(*m));
425  {
426  JLOG(j_.warn()) << "List for " << strHex(pubKey)
427  << " contained invalid validator manifest";
428  }
429  }
430 
431  // Cache the validator list in a file
432  CacheValidatorFile(pubKey, publisher);
433 
434  return applyResult;
435 }
436 
439 {
440  using namespace std::string_literals;
441  using namespace boost::filesystem;
442  using namespace boost::system::errc;
443 
445 
447  sites.reserve(publisherLists_.size());
448  for (auto const& [pubKey, publisher] : publisherLists_)
449  {
450  boost::system::error_code ec;
451 
452  if (publisher.available)
453  continue;
454 
455  boost::filesystem::path const filename = GetCacheFileName(pubKey);
456 
457  auto const fullPath{canonical(filename, ec)};
458  if (ec)
459  continue;
460 
461  auto size = file_size(fullPath, ec);
462  if (!ec && !size)
463  {
464  // Treat an empty file as a missing file, because
465  // nobody else is going to write it.
466  ec = make_error_code(no_such_file_or_directory);
467  }
468  if (ec)
469  continue;
470 
471  std::string const prefix = [&fullPath]() {
472 #if _MSC_VER // MSVC: Windows paths need a leading / added
473  {
474  return fullPath.root_path() == "/"s ? "file://" : "file:///";
475  }
476 #else
477  {
478  (void)fullPath;
479  return "file://";
480  }
481 #endif
482  }();
483  sites.emplace_back(prefix + fullPath.string());
484  }
485 
486  // Then let the ValidatorSites do the rest of the work.
487  return sites;
488 }
489 
492  Json::Value& list,
493  PublicKey& pubKey,
494  std::string const& manifest,
495  std::string const& blob,
496  std::string const& signature)
497 {
499 
500  if (!m || !publisherLists_.count(m->masterKey))
502 
503  pubKey = m->masterKey;
504  auto const revoked = m->revoked();
505 
506  auto const result = publisherManifests_.applyManifest(std::move(*m));
507 
508  if (revoked && result == ManifestDisposition::accepted)
509  {
510  removePublisherList(pubKey);
511  publisherLists_.erase(pubKey);
512  }
513 
514  if (revoked || result == ManifestDisposition::invalid)
516 
517  auto const sig = strUnHex(signature);
518  auto const data = base64_decode(blob);
519  if (!sig ||
522  makeSlice(data),
523  makeSlice(*sig)))
525 
526  Json::Reader r;
527  if (!r.parse(data, list))
529 
530  if (list.isMember("sequence") && list["sequence"].isInt() &&
531  list.isMember("expiration") && list["expiration"].isInt() &&
532  list.isMember("validators") && list["validators"].isArray())
533  {
534  auto const sequence = list["sequence"].asUInt();
535  auto const expiration = TimeKeeper::time_point{
536  TimeKeeper::duration{list["expiration"].asUInt()}};
537  if (sequence < publisherLists_[pubKey].sequence ||
538  expiration <= timeKeeper_.now())
539  return ListDisposition::stale;
540  else if (sequence == publisherLists_[pubKey].sequence)
542  }
543  else
544  {
546  }
547 
549 }
550 
551 bool
552 ValidatorList::listed(PublicKey const& identity) const
553 {
555 
556  auto const pubKey = validatorManifests_.getMasterKey(identity);
557  return keyListings_.find(pubKey) != keyListings_.end();
558 }
559 
560 bool
561 ValidatorList::trusted(PublicKey const& identity) const
562 {
564 
565  auto const pubKey = validatorManifests_.getMasterKey(identity);
566  return trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end();
567 }
568 
569 boost::optional<PublicKey>
571 {
573 
574  auto const pubKey = validatorManifests_.getMasterKey(identity);
575  if (keyListings_.find(pubKey) != keyListings_.end())
576  return pubKey;
577  return boost::none;
578 }
579 
580 boost::optional<PublicKey>
582 {
584 
585  auto const pubKey = validatorManifests_.getMasterKey(identity);
586  if (trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end())
587  return pubKey;
588  return boost::none;
589 }
590 
591 bool
593 {
595  return identity.size() && publisherLists_.count(identity);
596 }
597 
598 PublicKey
600 {
602  return localPubKey_;
603 }
604 
605 bool
607 {
608  auto const iList = publisherLists_.find(publisherKey);
609  if (iList == publisherLists_.end())
610  return false;
611 
612  JLOG(j_.debug()) << "Removing validator list for publisher "
613  << strHex(publisherKey);
614 
615  for (auto const& val : iList->second.list)
616  {
617  auto const& iVal = keyListings_.find(val);
618  if (iVal == keyListings_.end())
619  continue;
620 
621  if (iVal->second <= 1)
622  keyListings_.erase(iVal);
623  else
624  --iVal->second;
625  }
626 
627  iList->second.list.clear();
628  iList->second.available = false;
629 
630  return true;
631 }
632 
635 {
637  return publisherLists_.size();
638 }
639 
640 boost::optional<TimeKeeper::time_point>
642 {
644  boost::optional<TimeKeeper::time_point> res{boost::none};
645  for (auto const& p : publisherLists_)
646  {
647  // Unfetched
648  if (p.second.expiration == TimeKeeper::time_point{})
649  return boost::none;
650 
651  // Earliest
652  if (!res || p.second.expiration < *res)
653  res = p.second.expiration;
654  }
655  return res;
656 }
657 
660 {
662 
664 
665  res[jss::validation_quorum] = static_cast<Json::UInt>(quorum());
666 
667  {
668  auto& x = (res[jss::validator_list] = Json::objectValue);
669 
670  x[jss::count] = static_cast<Json::UInt>(count());
671 
672  if (auto when = expires())
673  {
674  if (*when == TimeKeeper::time_point::max())
675  {
676  x[jss::expiration] = "never";
677  x[jss::status] = "active";
678  }
679  else
680  {
681  x[jss::expiration] = to_string(*when);
682 
683  if (*when > timeKeeper_.now())
684  x[jss::status] = "active";
685  else
686  x[jss::status] = "expired";
687  }
688  }
689  else
690  {
691  x[jss::status] = "unknown";
692  x[jss::expiration] = "unknown";
693  }
694  }
695 
696  // Local static keys
697  PublicKey local;
698  Json::Value& jLocalStaticKeys =
699  (res[jss::local_static_keys] = Json::arrayValue);
700  if (auto it = publisherLists_.find(local); it != publisherLists_.end())
701  {
702  for (auto const& key : it->second.list)
703  jLocalStaticKeys.append(toBase58(TokenType::NodePublic, key));
704  }
705 
706  // Publisher lists
707  Json::Value& jPublisherLists =
708  (res[jss::publisher_lists] = Json::arrayValue);
709  for (auto const& p : publisherLists_)
710  {
711  if (local == p.first)
712  continue;
713  Json::Value& curr = jPublisherLists.append(Json::objectValue);
714  curr[jss::pubkey_publisher] = strHex(p.first);
715  curr[jss::available] = p.second.available;
716  curr[jss::uri] = p.second.siteUri;
717  if (p.second.expiration != TimeKeeper::time_point{})
718  {
719  curr[jss::seq] = static_cast<Json::UInt>(p.second.sequence);
720  curr[jss::expiration] = to_string(p.second.expiration);
721  curr[jss::version] = requiredListVersion;
722  }
723  Json::Value& keys = (curr[jss::list] = Json::arrayValue);
724  for (auto const& key : p.second.list)
725  {
727  }
728  }
729 
730  // Trusted validator keys
731  Json::Value& jValidatorKeys =
732  (res[jss::trusted_validator_keys] = Json::arrayValue);
733  for (auto const& k : trustedMasterKeys_)
734  {
735  jValidatorKeys.append(toBase58(TokenType::NodePublic, k));
736  }
737 
738  // signing keys
739  Json::Value& jSigningKeys = (res[jss::signing_keys] = Json::objectValue);
740  validatorManifests_.for_each_manifest([&jSigningKeys,
741  this](Manifest const& manifest) {
742  auto it = keyListings_.find(manifest.masterKey);
743  if (it != keyListings_.end())
744  {
745  jSigningKeys[toBase58(TokenType::NodePublic, manifest.masterKey)] =
747  }
748  });
749 
750  // Negative UNL
751  if (!negativeUNL_.empty())
752  {
753  Json::Value& jNegativeUNL = (res[jss::NegativeUNL] = Json::arrayValue);
754  for (auto const& k : negativeUNL_)
755  {
756  jNegativeUNL.append(toBase58(TokenType::NodePublic, k));
757  }
758  }
759 
760  return res;
761 }
762 
763 void
765  std::function<void(PublicKey const&, bool)> func) const
766 {
768 
769  for (auto const& v : keyListings_)
770  func(v.first, trusted(v.first));
771 }
772 
773 void
775  std::string const& manifest,
776  std::string const& blob,
777  std::string const& signature,
778  std::uint32_t version,
779  PublicKey const& pubKey,
780  std::size_t sequence,
781  uint256 const& hash)> func) const
782 {
784 
785  for (auto const& [key, pl] : publisherLists_)
786  {
787  if (!pl.available)
788  continue;
789  func(
790  pl.rawManifest,
791  pl.rawBlob,
792  pl.rawSignature,
793  pl.rawVersion,
794  key,
795  pl.sequence,
796  pl.hash);
797  }
798 }
799 
800 boost::optional<Json::Value>
801 ValidatorList::getAvailable(boost::beast::string_view const& pubKey)
802 {
804 
805  auto const keyBlob = strViewUnHex(pubKey);
806 
807  if (!keyBlob || !publicKeyType(makeSlice(*keyBlob)))
808  {
809  JLOG(j_.info()) << "Invalid requested validator list publisher key: "
810  << pubKey;
811  return {};
812  }
813 
814  auto id = PublicKey(makeSlice(*keyBlob));
815 
816  auto iter = publisherLists_.find(id);
817 
818  if (iter == publisherLists_.end() || !iter->second.available)
819  return {};
820 
822 
823  value["manifest"] = iter->second.rawManifest;
824  value["blob"] = iter->second.rawBlob;
825  value["signature"] = iter->second.rawSignature;
826  value["version"] = iter->second.rawVersion;
827 
828  return value;
829 }
830 
833  std::size_t unlSize,
834  std::size_t effectiveUnlSize,
835  std::size_t seenSize)
836 {
837  // Do not use achievable quorum until lists from all configured
838  // publishers are available
839  for (auto const& list : publisherLists_)
840  {
841  if (!list.second.available)
843  }
844 
845  // Use an 80% quorum to balance fork safety, liveness, and required UNL
846  // overlap.
847  //
848  // Theorem 8 of the Analysis of the XRP Ledger Consensus Protocol
849  // (https://arxiv.org/abs/1802.07242) says:
850  // XRP LCP guarantees fork safety if Oi,j > nj/2 + ni − qi + ti,j for
851  // every pair of nodes Pi, Pj.
852  //
853  // ni: size of Pi's UNL
854  // nj: size of Pj's UNL
855  // Oi,j: number of validators in both UNLs
856  // qi: validation quorum for Pi's UNL
857  // ti, tj: maximum number of allowed Byzantine faults in Pi and Pj's UNLs
858  // ti,j: min{ti, tj, Oi,j}
859  //
860  // Assume ni < nj, meaning and ti,j = ti
861  //
862  // For qi = .8*ni, we make ti <= .2*ni
863  // (We could make ti lower and tolerate less UNL overlap. However in order
864  // to prioritize safety over liveness, we need ti >= ni - qi)
865  //
866  // An 80% quorum allows two UNLs to safely have < .2*ni unique validators
867  // between them:
868  //
869  // pi = ni - Oi,j
870  // pj = nj - Oi,j
871  //
872  // Oi,j > nj/2 + ni − qi + ti,j
873  // ni - pi > (ni - pi + pj)/2 + ni − .8*ni + .2*ni
874  // pi + pj < .2*ni
875  //
876  // Note that the negative UNL protocol introduced the AbsoluteMinimumQuorum
877  // which is 60% of the original UNL size. The effective quorum should
878  // not be lower than it.
879  auto quorum = static_cast<std::size_t>(std::max(
880  std::ceil(effectiveUnlSize * 0.8f), std::ceil(unlSize * 0.6f)));
881 
882  // Use lower quorum specified via command line if the normal quorum appears
883  // unreachable based on the number of recently received validations.
884  if (minimumQuorum_ && *minimumQuorum_ < quorum && seenSize < quorum)
885  {
887 
888  JLOG(j_.warn()) << "Using unsafe quorum of " << quorum
889  << " as specified in the command line";
890  }
891 
892  return quorum;
893 }
894 
897 {
899 
900  // Remove any expired published lists
901  for (auto const& list : publisherLists_)
902  {
903  if (list.second.available &&
904  list.second.expiration <= timeKeeper_.now())
905  removePublisherList(list.first);
906  }
907 
908  TrustChanges trustChanges;
909 
910  auto it = trustedMasterKeys_.cbegin();
911  while (it != trustedMasterKeys_.cend())
912  {
913  if (!keyListings_.count(*it) || validatorManifests_.revoked(*it))
914  {
915  trustChanges.removed.insert(calcNodeID(*it));
916  it = trustedMasterKeys_.erase(it);
917  }
918  else
919  {
920  ++it;
921  }
922  }
923 
924  for (auto const& val : keyListings_)
925  {
926  if (!validatorManifests_.revoked(val.first) &&
927  trustedMasterKeys_.emplace(val.first).second)
928  trustChanges.added.insert(calcNodeID(val.first));
929  }
930 
931  // If there were any changes, we need to update the ephemeral signing keys:
932  if (!trustChanges.added.empty() || !trustChanges.removed.empty())
933  {
934  trustedSigningKeys_.clear();
935 
936  for (auto const& k : trustedMasterKeys_)
938  }
939 
940  JLOG(j_.debug())
941  << trustedMasterKeys_.size() << " of " << keyListings_.size()
942  << " listed validators eligible for inclusion in the trusted set";
943 
944  auto unlSize = trustedMasterKeys_.size();
945  auto effectiveUnlSize = unlSize;
946  auto seenSize = seenValidators.size();
947  if (!negativeUNL_.empty())
948  {
949  for (auto const& k : trustedMasterKeys_)
950  {
951  if (negativeUNL_.count(k))
952  --effectiveUnlSize;
953  }
954  hash_set<NodeID> negUnlNodeIDs;
955  for (auto const& k : negativeUNL_)
956  {
957  negUnlNodeIDs.emplace(calcNodeID(k));
958  }
959  for (auto const& nid : seenValidators)
960  {
961  if (negUnlNodeIDs.count(nid))
962  --seenSize;
963  }
964  }
965  quorum_ = calculateQuorum(unlSize, effectiveUnlSize, seenSize);
966 
967  JLOG(j_.debug()) << "Using quorum of " << quorum_ << " for new set of "
968  << trustedMasterKeys_.size() << " trusted validators ("
969  << trustChanges.added.size() << " added, "
970  << trustChanges.removed.size() << " removed)";
971 
972  if (trustedMasterKeys_.size() < quorum_)
973  {
974  JLOG(j_.warn()) << "New quorum of " << quorum_
975  << " exceeds the number of trusted validators ("
976  << trustedMasterKeys_.size() << ")";
977  }
978 
979  return trustChanges;
980 }
981 
984 {
985  std::shared_lock lock{mutex_};
986  return trustedMasterKeys_;
987 }
988 
991 {
992  std::shared_lock lock{mutex_};
993  return negativeUNL_;
994 }
995 
996 void
998 {
999  std::lock_guard lock{mutex_};
1000  negativeUNL_ = negUnl;
1001 }
1002 
1005  std::vector<std::shared_ptr<STValidation>>&& validations) const
1006 {
1007  // Remove validations that are from validators on the negative UNL.
1008  auto ret = std::move(validations);
1009 
1010  std::shared_lock lock{mutex_};
1011  if (!negativeUNL_.empty())
1012  {
1013  ret.erase(
1015  ret.begin(),
1016  ret.end(),
1017  [&](auto const& v) -> bool {
1018  if (auto const masterKey =
1019  getTrustedKey(v->getSignerPublic());
1020  masterKey)
1021  {
1022  return negativeUNL_.count(*masterKey);
1023  }
1024  else
1025  {
1026  return false;
1027  }
1028  }),
1029  ret.end());
1030  }
1031 
1032  return ret;
1033 }
1034 
1035 } // namespace ripple
Json::Value::isInt
bool isInt() const
Definition: json_value.cpp:979
ripple::ValidatorList::getListedKey
boost::optional< PublicKey > getListedKey(PublicKey const &identity) const
Returns listed master public if public key is included on any lists.
Definition: ValidatorList.cpp:570
ripple::ValidatorList::validatorManifests_
ManifestCache & validatorManifests_
Definition: ValidatorList.h:137
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:240
ripple::ValidatorList::for_each_listed
void for_each_listed(std::function< void(PublicKey const &, bool)> func) const
Invokes the callback once for every listed validation public key.
Definition: ValidatorList.cpp:764
ripple::HashRouter::addSuppressionPeer
bool addSuppressionPeer(uint256 const &key, PeerShortID peer)
Definition: HashRouter.cpp:51
ripple::ValidatorList::localPublicKey
PublicKey localPublicKey() const
Returns local validator public key.
Definition: ValidatorList.cpp:599
std::string
STL class.
std::shared_ptr
STL class.
ripple::ListDisposition
ListDisposition
Definition: ValidatorList.h:43
ripple::ManifestCache::getMasterKey
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
Definition: app/misc/impl/Manifest.cpp:301
ripple::calcNodeID
NodeID calcNodeID(PublicKey const &pk)
Calculate the 160-bit node ID from a node public key.
Definition: PublicKey.cpp:299
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::publicKeyType
boost::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:203
ripple::ValidatorList::PublisherList::rawSignature
std::string rawSignature
Definition: ValidatorList.h:132
ripple::TrustChanges
Changes in trusted nodes after updating validator list.
Definition: ValidatorList.h:68
ripple::Manifest
Definition: Manifest.h:78
ripple::ListDisposition::stale
@ stale
Trusted publisher key, but seq is too old.
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::ValidatorList::publisherLists_
hash_map< PublicKey, PublisherList > publisherLists_
Definition: ValidatorList.h:148
std::unordered_set
STL class.
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
std::vector::reserve
T reserve(T... args)
ripple::ValidatorList::getAvailable
boost::optional< Json::Value > getAvailable(boost::beast::string_view const &pubKey)
Returns the current valid list for the given publisher key, if available, as a Json object.
Definition: ValidatorList.cpp:801
ripple::HashPrefix::manifest
@ manifest
Manifest.
ripple::ValidatorList::GetCacheFileName
boost::filesystem::path GetCacheFileName(PublicKey const &pubKey)
Get the filename used for caching UNLs.
Definition: ValidatorList.cpp:198
Json::UInt
unsigned int UInt
Definition: json_forwards.h:27
std::vector< std::string >
std::unordered_set::size
T size(T... args)
ripple::ValidatorList::PublisherList::rawManifest
std::string rawManifest
Definition: ValidatorList.h:130
ripple::ValidatorList::trustedPublisher
bool trustedPublisher(PublicKey const &identity) const
Returns true if public key is a trusted publisher.
Definition: ValidatorList.cpp:592
ripple::ValidatorList::setNegativeUNL
void setNegativeUNL(hash_set< PublicKey > const &negUnl)
set the Negative UNL with validators' master public keys
Definition: ValidatorList.cpp:997
ripple::ValidatorList::PublisherListStats
Describes the result of processing a Validator List (UNL), including some of the information from the...
Definition: ValidatorList.h:185
ripple::ValidatorList::negativeUNL_
hash_set< PublicKey > negativeUNL_
Definition: ValidatorList.h:164
ripple::ValidatorList::filePrefix_
static const std::string filePrefix_
Definition: ValidatorList.h:168
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::ValidatorList::updateTrusted
TrustChanges updateTrusted(hash_set< NodeID > const &seenValidators)
Update trusted nodes.
Definition: ValidatorList.cpp:896
ripple::ValidatorList::j_
const beast::Journal j_
Definition: ValidatorList.h:141
ripple::Slice::data
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition: Slice.h:96
ripple::ValidatorList::getTrustedMasterKeys
hash_set< PublicKey > getTrustedMasterKeys() const
get the trusted master public keys
Definition: ValidatorList.cpp:983
std::unordered_set::emplace
T emplace(T... args)
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::ValidatorList::verify
ListDisposition verify(Json::Value &list, PublicKey &pubKey, std::string const &manifest, std::string const &blob, std::string const &signature)
Check response for trusted valid published list.
Definition: ValidatorList.cpp:491
std::lock_guard
STL class.
ripple::strUnHex
boost::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Definition: StringUtilities.h:72
std::function
Json::Value::toStyledString
std::string toStyledString() const
Definition: json_value.cpp:1039
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:42
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
cmath
std::sort
T sort(T... args)
ripple::HashRouter
Routing table for objects identified by hash.
Definition: HashRouter.h:52
ripple::ValidatorList::mutex_
std::shared_timed_mutex mutex_
Definition: ValidatorList.h:142
std::vector::clear
T clear(T... args)
std::vector::push_back
T push_back(T... args)
ripple::ValidatorList::ValidatorList
ValidatorList(ManifestCache &validatorManifests, ManifestCache &publisherManifests, TimeKeeper &timeKeeper, std::string const &databasePath, beast::Journal j, boost::optional< std::size_t > minimumQuorum=boost::none)
Definition: ValidatorList.cpp:64
ripple::ValidatorList::getTrustedKey
boost::optional< PublicKey > getTrustedKey(PublicKey const &identity) const
Returns master public key if public key is trusted.
Definition: ValidatorList.cpp:581
ripple::verify
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical)
Verify a signature on a message.
Definition: PublicKey.cpp:268
ripple::base_uint< 256 >
ripple::ValidatorList::CacheValidatorFile
void CacheValidatorFile(PublicKey const &pubKey, PublisherList const &publisher)
Write a JSON UNL to a cache file.
Definition: ValidatorList.cpp:204
ripple::ValidatorList::PublisherList::rawVersion
std::uint32_t rawVersion
Definition: ValidatorList.h:133
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::ValidatorList::dataPath_
const boost::filesystem::path dataPath_
Definition: ValidatorList.h:140
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::ValidatorList::applyList
PublisherListStats applyList(std::string const &manifest, std::string const &blob, std::string const &signature, std::uint32_t version, std::string siteUri, boost::optional< uint256 > const &hash={})
Apply published list of public keys.
Definition: ValidatorList.cpp:296
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::PublicKey::size
std::size_t size() const noexcept
Definition: PublicKey.h:87
ripple::TrustChanges::removed
hash_set< NodeID > removed
Definition: ValidatorList.h:73
ripple::ValidatorList::minimumQuorum_
boost::optional< std::size_t > minimumQuorum_
Definition: ValidatorList.h:145
std::unique_lock
STL class.
ripple::ValidatorList::publisherManifests_
ManifestCache & publisherManifests_
Definition: ValidatorList.h:138
ripple::ManifestCache::revoked
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
Definition: app/misc/impl/Manifest.cpp:349
ripple::ValidatorList::timeKeeper_
TimeKeeper & timeKeeper_
Definition: ValidatorList.h:139
ripple::ValidatorList::listed
bool listed(PublicKey const &identity) const
Returns true if public key is included on any lists.
Definition: ValidatorList.cpp:552
ripple::writeFileContents
void writeFileContents(boost::system::error_code &ec, boost::filesystem::path const &destPath, std::string const &contents)
Definition: FileUtilities.cpp:66
ripple::base64_decode
std::string base64_decode(std::string const &data)
Definition: base64.cpp:245
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
ripple::ManifestCache::for_each_manifest
void for_each_manifest(Function &&f) const
Invokes the callback once for every populated manifest.
Definition: Manifest.h:369
ripple::Overlay::getActivePeers
virtual PeerSequence getActivePeers() const =0
Returns a sequence representing the current list of peers.
ripple::ListDisposition::untrusted
@ untrusted
List signed by untrusted publisher key.
ripple::ManifestCache::getSigningKey
PublicKey getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
Definition: app/misc/impl/Manifest.cpp:289
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::ValidatorList::requiredListVersion
static constexpr std::uint32_t requiredListVersion
Definition: ValidatorList.h:167
ripple::ValidatorList::applyListAndBroadcast
PublisherListStats applyListAndBroadcast(std::string const &manifest, std::string const &blob, std::string const &signature, std::uint32_t version, std::string siteUri, uint256 const &hash, Overlay &overlay, HashRouter &hashRouter)
Apply published list of public keys, then broadcast it to all peers that have not seen it or sent it.
Definition: ValidatorList.cpp:233
std::uint32_t
ripple::HashRouter::shouldRelay
boost::optional< std::set< PeerShortID > > shouldRelay(uint256 const &key)
Determines whether the hashed item should be relayed.
Definition: HashRouter.cpp:112
std::forward_as_tuple
T forward_as_tuple(T... args)
std::remove_if
T remove_if(T... args)
ripple::TimeKeeper
Manages various times used by the server.
Definition: TimeKeeper.h:32
ripple::ListDisposition::unsupported_version
@ unsupported_version
List version is not supported.
std::ceil
T ceil(T... args)
ripple::ManifestDisposition::invalid
@ invalid
Timely, but invalid signature.
ripple::ValidatorList::negativeUNLFilter
std::vector< std::shared_ptr< STValidation > > negativeUNLFilter(std::vector< std::shared_ptr< STValidation >> &&validations) const
Remove validations that are from validators on the negative UNL.
Definition: ValidatorList.cpp:1004
Json::Value::isArray
bool isArray() const
Definition: json_value.cpp:1015
ripple::ValidatorList::PublisherList::rawBlob
std::string rawBlob
Definition: ValidatorList.h:131
ripple::ValidatorList::calculateQuorum
std::size_t calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize)
Return quorum for trusted validator set.
Definition: ValidatorList.cpp:832
ripple::ManifestDisposition::accepted
@ accepted
Manifest is valid.
ripple::ValidatorList::localPubKey_
PublicKey localPubKey_
Definition: ValidatorList.h:161
ripple::ManifestCache
Remembers manifests with the highest sequence number.
Definition: Manifest.h:209
ripple::ValidatorList::loadLists
std::vector< std::string > loadLists()
Definition: ValidatorList.cpp:438
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::ValidatorList::trustedSigningKeys_
hash_set< PublicKey > trustedSigningKeys_
Definition: ValidatorList.h:159
ripple::deserializeManifest
boost::optional< Manifest > deserializeManifest(Slice s)
Constructs Manifest from serialized string.
Definition: app/misc/impl/Manifest.cpp:38
ripple::ValidatorList::load
bool load(PublicKey const &localSigningKey, std::vector< std::string > const &configKeys, std::vector< std::string > const &publisherKeys)
Load configured trusted keys.
Definition: ValidatorList.cpp:82
ripple::ValidatorList::for_each_available
void for_each_available(std::function< void(std::string const &manifest, std::string const &blob, std::string const &signature, std::uint32_t version, PublicKey const &pubKey, std::size_t sequence, uint256 const &hash)> func) const
Invokes the callback once for every available publisher list's raw data members.
Definition: ValidatorList.cpp:774
ripple::Overlay
Manages the set of connected peers.
Definition: Overlay.h:52
ripple::ListDisposition::same_sequence
@ same_sequence
Same sequence as current list.
std::vector::begin
T begin(T... args)
ripple::strViewUnHex
boost::optional< Blob > strViewUnHex(boost::string_view const &strSrc)
Definition: StringUtilities.h:118
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
ripple::ListDisposition::invalid
@ invalid
Invalid format or signature.
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:73
ripple::TimeKeeper::now
virtual time_point now() const override=0
Returns the estimate of wall time, in network time.
std::unordered_set::count
T count(T... args)
ripple::TrustChanges::added
hash_set< NodeID > added
Definition: ValidatorList.h:72
std::vector::empty
T empty(T... args)
ripple::TokenType::NodePublic
@ NodePublic
mutex
ripple::ValidatorList::getJson
Json::Value getJson() const
Return a JSON representation of the state of the validator list.
Definition: ValidatorList.cpp:659
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::ProtocolFeature::ValidatorListPropagation
@ ValidatorListPropagation
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:67
std::vector::end
T end(T... args)
std::numeric_limits::max
T max(T... args)
ripple::ValidatorList::expires
boost::optional< TimeKeeper::time_point > expires() const
Return the time when the validator list will expire.
Definition: ValidatorList.cpp:641
ripple::ValidatorList::trusted
bool trusted(PublicKey const &identity) const
Returns true if public key is trusted.
Definition: ValidatorList.cpp:561
ripple::ValidatorList::trustedMasterKeys_
hash_set< PublicKey > trustedMasterKeys_
Definition: ValidatorList.h:154
ripple::ValidatorList::getNegativeUNL
hash_set< PublicKey > getNegativeUNL() const
get the master public keys of Negative UNL validators
Definition: ValidatorList.cpp:990
ripple::ValidatorList::count
std::size_t count() const
Return the number of configured validator list sites.
Definition: ValidatorList.cpp:634
ripple::ManifestCache::applyManifest
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
Definition: app/misc/impl/Manifest.cpp:361
shared_mutex
beast::abstract_clock< NetClock >::time_point
typename NetClock ::time_point time_point
Definition: abstract_clock.h:63
ripple::ValidatorList::keyListings_
hash_map< PublicKey, std::size_t > keyListings_
Definition: ValidatorList.h:151
ripple::ListDisposition::accepted
@ accepted
List is valid.
ripple::ValidatorList::quorum_
std::atomic< std::size_t > quorum_
Definition: ValidatorList.h:144
beast::abstract_clock< NetClock >::duration
typename NetClock ::duration duration
Definition: abstract_clock.h:62
std::shared_lock
STL class.
ripple::ValidatorList::PublisherList
Definition: ValidatorList.h:121
ripple::ValidatorList::quorum
std::size_t quorum() const
Get quorum value for current trusted key set.
Definition: ValidatorList.h:342
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::ValidatorList::removePublisherList
bool removePublisherList(PublicKey const &publisherKey)
Stop trusting publisher's list of keys.
Definition: ValidatorList.cpp:606