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