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/NetworkOPs.h>
22 #include <ripple/app/misc/ValidatorList.h>
23 #include <ripple/basics/FileUtilities.h>
24 #include <ripple/basics/Slice.h>
25 #include <ripple/basics/StringUtilities.h>
26 #include <ripple/basics/base64.h>
27 #include <ripple/json/json_reader.h>
28 #include <ripple/overlay/Overlay.h>
29 #include <ripple/protocol/STValidation.h>
30 #include <ripple/protocol/digest.h>
31 #include <ripple/protocol/jss.h>
32 #include <ripple/protocol/messages.h>
33 #include <boost/regex.hpp>
34 
35 #include <cmath>
36 #include <mutex>
37 #include <numeric>
38 #include <shared_mutex>
39 
40 namespace ripple {
41 
44 {
45  switch (disposition)
46  {
48  return "accepted";
50  return "expired";
52  return "same_sequence";
54  return "pending";
56  return "known_sequence";
58  return "unsupported_version";
60  return "untrusted";
62  return "stale";
64  return "invalid";
65  }
66  return "unknown";
67 }
68 
70 {
71  ++dispositions[d];
72 }
73 
76  PublicKey key,
77  PublisherStatus stat,
78  std::size_t seq)
79  : publisherKey(key), status(stat), sequence(seq)
80 {
81  ++dispositions[d];
82 }
83 
86 {
87  return dispositions.empty() ? ListDisposition::invalid
88  : dispositions.begin()->first;
89 }
90 
93 {
94  return dispositions.empty() ? ListDisposition::invalid
95  : dispositions.rbegin()->first;
96 }
97 
98 void
100  PublisherListStats const& src)
101 {
102  for (auto const& [disp, count] : src.dispositions)
103  {
104  dispositions[disp] += count;
105  }
106 }
107 
109  std::shared_ptr<Message> const& message_,
110  uint256 hash_,
111  std::size_t num_)
112  : message(message_), hash(hash_), numVLs(num_)
113 {
114 }
115 
116 const std::string ValidatorList::filePrefix_ = "cache.";
117 
119  ManifestCache& validatorManifests,
120  ManifestCache& publisherManifests,
121  TimeKeeper& timeKeeper,
122  std::string const& databasePath,
123  beast::Journal j,
124  std::optional<std::size_t> minimumQuorum)
125  : validatorManifests_(validatorManifests)
126  , publisherManifests_(publisherManifests)
127  , timeKeeper_(timeKeeper)
128  , dataPath_(databasePath)
129  , j_(j)
130  , quorum_(minimumQuorum.value_or(1)) // Genesis ledger quorum
131  , minimumQuorum_(minimumQuorum)
132 {
133 }
134 
135 bool
137  PublicKey const& localSigningKey,
138  std::vector<std::string> const& configKeys,
139  std::vector<std::string> const& publisherKeys)
140 {
141  static boost::regex const re(
142  "[[:space:]]*" // skip leading whitespace
143  "([[:alnum:]]+)" // node identity
144  "(?:" // begin optional comment block
145  "[[:space:]]+" // (skip all leading whitespace)
146  "(?:" // begin optional comment
147  "(.*[^[:space:]]+)" // the comment
148  "[[:space:]]*" // (skip all trailing whitespace)
149  ")?" // end optional comment
150  ")?" // end optional comment block
151  );
152 
153  std::lock_guard lock{mutex_};
154 
155  JLOG(j_.debug())
156  << "Loading configured trusted validator list publisher keys";
157 
158  std::size_t count = 0;
159  for (auto key : publisherKeys)
160  {
161  JLOG(j_.trace()) << "Processing '" << key << "'";
162 
163  auto const ret = strUnHex(key);
164 
165  if (!ret || !publicKeyType(makeSlice(*ret)))
166  {
167  JLOG(j_.error()) << "Invalid validator list publisher key: " << key;
168  return false;
169  }
170 
171  auto id = PublicKey(makeSlice(*ret));
172  auto status = PublisherStatus::unavailable;
173 
175  {
176  JLOG(j_.warn())
177  << "Configured validator list publisher key is revoked: "
178  << key;
179  status = PublisherStatus::revoked;
180  }
181 
182  if (publisherLists_.count(id))
183  {
184  JLOG(j_.warn())
185  << "Duplicate validator list publisher key: " << key;
186  continue;
187  }
188 
189  publisherLists_[id].status = status;
190  ++count;
191  }
192 
193  JLOG(j_.debug()) << "Loaded " << count << " keys";
194 
195  localPubKey_ = validatorManifests_.getMasterKey(localSigningKey);
196 
197  // Treat local validator key as though it was listed in the config
198  if (localPubKey_.size())
199  keyListings_.insert({localPubKey_, 1});
200 
201  JLOG(j_.debug()) << "Loading configured validator keys";
202 
203  count = 0;
204  for (auto const& n : configKeys)
205  {
206  JLOG(j_.trace()) << "Processing '" << n << "'";
207 
208  boost::smatch match;
209 
210  if (!boost::regex_match(n, match, re))
211  {
212  JLOG(j_.error()) << "Malformed entry: '" << n << "'";
213  return false;
214  }
215 
216  auto const id = parseBase58<PublicKey>(TokenType::NodePublic, match[1]);
217 
218  if (!id)
219  {
220  JLOG(j_.error()) << "Invalid node identity: " << match[1];
221  return false;
222  }
223 
224  // Skip local key which was already added
225  if (*id == localPubKey_ || *id == localSigningKey)
226  continue;
227 
228  auto ret = keyListings_.insert({*id, 1});
229  if (!ret.second)
230  {
231  JLOG(j_.warn()) << "Duplicate node identity: " << match[1];
232  continue;
233  }
234  auto [it, inserted] = publisherLists_.emplace();
235  // Config listed keys never expire
236  auto& current = it->second.current;
237  if (inserted)
238  current.validUntil = TimeKeeper::time_point::max();
239  current.list.emplace_back(*id);
240  it->second.status = PublisherStatus::available;
241  ++count;
242  }
243 
244  JLOG(j_.debug()) << "Loaded " << count << " entries";
245 
246  return true;
247 }
248 
249 boost::filesystem::path
252  PublicKey const& pubKey) const
253 {
254  return dataPath_ / (filePrefix_ + strHex(pubKey));
255 }
256 
257 // static
260  std::string const& pubKey,
261  ValidatorList::PublisherListCollection const& pubCollection,
262  beast::Journal j)
263 {
264  return buildFileData(pubKey, pubCollection, {}, j);
265 }
266 
267 // static
270  std::string const& pubKey,
271  ValidatorList::PublisherListCollection const& pubCollection,
272  std::optional<std::uint32_t> forceVersion,
273  beast::Journal j)
274 {
276 
277  assert(pubCollection.rawVersion == 2 || pubCollection.remaining.empty());
278  auto const effectiveVersion =
279  forceVersion ? *forceVersion : pubCollection.rawVersion;
280 
281  value[jss::manifest] = pubCollection.rawManifest;
282  value[jss::version] = effectiveVersion;
283  value[jss::public_key] = pubKey;
284 
285  switch (effectiveVersion)
286  {
287  case 1: {
288  auto const& current = pubCollection.current;
289  value[jss::blob] = current.rawBlob;
290  value[jss::signature] = current.rawSignature;
291  // This is only possible if "downgrading" a v2 UNL to v1, for
292  // example for the /vl/ endpoint.
293  if (current.rawManifest &&
294  *current.rawManifest != pubCollection.rawManifest)
295  value[jss::manifest] = *current.rawManifest;
296  break;
297  }
298  case 2: {
300 
301  auto add = [&blobs, &outerManifest = pubCollection.rawManifest](
302  PublisherList const& pubList) {
303  auto& blob = blobs.append(Json::objectValue);
304  blob[jss::blob] = pubList.rawBlob;
305  blob[jss::signature] = pubList.rawSignature;
306  if (pubList.rawManifest &&
307  *pubList.rawManifest != outerManifest)
308  blob[jss::manifest] = *pubList.rawManifest;
309  };
310 
311  add(pubCollection.current);
312  for (auto const& [_, pending] : pubCollection.remaining)
313  {
314  (void)_;
315  add(pending);
316  }
317 
318  value[jss::blobs_v2] = std::move(blobs);
319  break;
320  }
321  default:
322  JLOG(j.trace())
323  << "Invalid VL version provided: " << effectiveVersion;
324  value = Json::nullValue;
325  }
326 
327  return value;
328 }
329 
330 void
332  ValidatorList::lock_guard const& lock,
333  PublicKey const& pubKey) const
334 {
335  if (dataPath_.empty())
336  return;
337 
338  boost::filesystem::path const filename = getCacheFileName(lock, pubKey);
339 
340  boost::system::error_code ec;
341 
342  Json::Value value =
343  buildFileData(strHex(pubKey), publisherLists_.at(pubKey), j_);
344  // rippled should be the only process writing to this file, so
345  // if it ever needs to be read, it is not expected to change externally, so
346  // delay the refresh as long as possible: 24 hours. (See also
347  // `ValidatorSite::missingSite()`)
348  value[jss::refresh_interval] = 24 * 60;
349 
350  writeFileContents(ec, filename, value.toStyledString());
351 
352  if (ec)
353  {
354  // Log and ignore any file I/O exceptions
355  JLOG(j_.error()) << "Problem writing " << filename << " " << ec.value()
356  << ": " << ec.message();
357  }
358 }
359 
360 // static
363 {
365  switch (version)
366  {
367  case 1: {
368  if (!body.isMember(jss::blob) || !body[jss::blob].isString() ||
369  !body.isMember(jss::signature) ||
370  !body[jss::signature].isString() ||
371  // If the v2 field is present, the VL is malformed
372  body.isMember(jss::blobs_v2))
373  return {};
374  ValidatorBlobInfo& info = result.emplace_back();
375  info.blob = body[jss::blob].asString();
376  info.signature = body[jss::signature].asString();
377  assert(result.size() == 1);
378  return result;
379  }
380  // Treat unknown versions as if they're the latest version. This
381  // will likely break a bunch of unit tests each time we introduce a
382  // new version, so don't do it casually. Note that the version is
383  // validated elsewhere.
384  case 2:
385  default: {
386  if (!body.isMember(jss::blobs_v2) ||
387  !body[jss::blobs_v2].isArray() ||
388  body[jss::blobs_v2].size() > maxSupportedBlobs ||
389  // If any of the v1 fields are present, the VL is malformed
390  body.isMember(jss::blob) || body.isMember(jss::signature))
391  return {};
392  auto const& blobs = body[jss::blobs_v2];
393  result.reserve(blobs.size());
394  for (auto const& blobInfo : blobs)
395  {
396  if (!blobInfo.isObject() ||
397  !blobInfo.isMember(jss::signature) ||
398  !blobInfo[jss::signature].isString() ||
399  !blobInfo.isMember(jss::blob) ||
400  !blobInfo[jss::blob].isString())
401  return {};
402  ValidatorBlobInfo& info = result.emplace_back();
403  info.blob = blobInfo[jss::blob].asString();
404  info.signature = blobInfo[jss::signature].asString();
405  if (blobInfo.isMember(jss::manifest))
406  {
407  if (!blobInfo[jss::manifest].isString())
408  return {};
409  info.manifest = blobInfo[jss::manifest].asString();
410  }
411  }
412  assert(result.size() == blobs.size());
413  return result;
414  }
415  }
416 }
417 
418 // static
420 ValidatorList::parseBlobs(protocol::TMValidatorList const& body)
421 {
422  return {{body.blob(), body.signature(), {}}};
423 }
424 
425 // static
427 ValidatorList::parseBlobs(protocol::TMValidatorListCollection const& body)
428 {
429  if (body.blobs_size() > maxSupportedBlobs)
430  return {};
432  result.reserve(body.blobs_size());
433  for (auto const& blob : body.blobs())
434  {
435  ValidatorBlobInfo& info = result.emplace_back();
436  info.blob = blob.blob();
437  info.signature = blob.signature();
438  if (blob.has_manifest())
439  {
440  info.manifest = blob.manifest();
441  }
442  }
443  assert(result.size() == body.blobs_size());
444  return result;
445 }
446 
450  protocol::TMValidatorListCollection const& largeMsg,
451  std::size_t maxSize,
452  std::size_t begin,
453  std::size_t end);
454 
458  protocol::TMValidatorListCollection const& largeMsg,
459  std::size_t maxSize,
460  std::size_t begin = 0,
461  std::size_t end = 0)
462 {
463  if (begin == 0 && end == 0)
464  end = largeMsg.blobs_size();
465  assert(begin < end);
466  if (end <= begin)
467  return 0;
468 
469  auto mid = (begin + end) / 2;
470  // The parts function will do range checking
471  // Use two separate calls to ensure deterministic order
472  auto result = splitMessageParts(messages, largeMsg, maxSize, begin, mid);
473  return result + splitMessageParts(messages, largeMsg, maxSize, mid, end);
474 }
475 
479  protocol::TMValidatorListCollection const& largeMsg,
480  std::size_t maxSize,
481  std::size_t begin,
482  std::size_t end)
483 {
484  if (end <= begin)
485  return 0;
486  if (end - begin == 1)
487  {
488  protocol::TMValidatorList smallMsg;
489  smallMsg.set_version(1);
490  smallMsg.set_manifest(largeMsg.manifest());
491 
492  auto const& blob = largeMsg.blobs(begin);
493  smallMsg.set_blob(blob.blob());
494  smallMsg.set_signature(blob.signature());
495  // This is only possible if "downgrading" a v2 UNL to v1.
496  if (blob.has_manifest())
497  smallMsg.set_manifest(blob.manifest());
498 
499  assert(Message::totalSize(smallMsg) <= maximiumMessageSize);
500 
501  messages.emplace_back(
502  std::make_shared<Message>(smallMsg, protocol::mtVALIDATORLIST),
503  sha512Half(smallMsg),
504  1);
505  return messages.back().numVLs;
506  }
507  else
508  {
510  smallMsg.emplace();
511  smallMsg->set_version(largeMsg.version());
512  smallMsg->set_manifest(largeMsg.manifest());
513 
514  for (std::size_t i = begin; i < end; ++i)
515  {
516  *smallMsg->add_blobs() = largeMsg.blobs(i);
517  }
518 
519  if (Message::totalSize(*smallMsg) > maxSize)
520  {
521  // free up the message space
522  smallMsg.reset();
523  return splitMessage(messages, largeMsg, maxSize, begin, end);
524  }
525  else
526  {
527  messages.emplace_back(
528  std::make_shared<Message>(
529  *smallMsg, protocol::mtVALIDATORLISTCOLLECTION),
530  sha512Half(*smallMsg),
531  smallMsg->blobs_size());
532  return messages.back().numVLs;
533  }
534  }
535  return 0;
536 }
537 
538 // Build a v1 protocol message using only the current VL
542  std::uint32_t rawVersion,
543  std::string const& rawManifest,
544  ValidatorBlobInfo const& currentBlob,
545  std::size_t maxSize)
546 {
547  assert(messages.empty());
548  protocol::TMValidatorList msg;
549  auto const manifest =
550  currentBlob.manifest ? *currentBlob.manifest : rawManifest;
551  auto const version = 1;
552  msg.set_manifest(manifest);
553  msg.set_blob(currentBlob.blob);
554  msg.set_signature(currentBlob.signature);
555  // Override the version
556  msg.set_version(version);
557 
558  assert(Message::totalSize(msg) <= maximiumMessageSize);
559  messages.emplace_back(
560  std::make_shared<Message>(msg, protocol::mtVALIDATORLIST),
561  sha512Half(msg),
562  1);
563  return 1;
564 }
565 
566 // Build a v2 protocol message using all the VLs with sequence larger than the
567 // peer's
571  std::uint64_t peerSequence,
572  std::uint32_t rawVersion,
573  std::string const& rawManifest,
575  std::size_t maxSize)
576 {
577  assert(messages.empty());
578  protocol::TMValidatorListCollection msg;
579  auto const version = rawVersion < 2 ? 2 : rawVersion;
580  msg.set_version(version);
581  msg.set_manifest(rawManifest);
582 
583  for (auto const& [sequence, blobInfo] : blobInfos)
584  {
585  if (sequence <= peerSequence)
586  continue;
587  protocol::ValidatorBlobInfo& blob = *msg.add_blobs();
588  blob.set_blob(blobInfo.blob);
589  blob.set_signature(blobInfo.signature);
590  if (blobInfo.manifest)
591  blob.set_manifest(*blobInfo.manifest);
592  }
593  assert(msg.blobs_size() > 0);
594  if (Message::totalSize(msg) > maxSize)
595  {
596  // split into smaller messages
597  return splitMessage(messages, msg, maxSize);
598  }
599  else
600  {
601  messages.emplace_back(
602  std::make_shared<Message>(msg, protocol::mtVALIDATORLISTCOLLECTION),
603  sha512Half(msg),
604  msg.blobs_size());
605  return messages.back().numVLs;
606  }
607 }
608 
609 [[nodiscard]]
610 // static
613  std::size_t messageVersion,
614  std::uint64_t peerSequence,
615  std::size_t maxSequence,
616  std::uint32_t rawVersion,
617  std::string const& rawManifest,
620  std::size_t maxSize /*= maximiumMessageSize*/)
621 {
622  assert(!blobInfos.empty());
623  auto const& [currentSeq, currentBlob] = *blobInfos.begin();
624  auto numVLs = std::accumulate(
625  messages.begin(),
626  messages.end(),
627  0,
628  [](std::size_t total, MessageWithHash const& m) {
629  return total + m.numVLs;
630  });
631  if (messageVersion == 2 && peerSequence < maxSequence)
632  {
633  // Version 2
634  if (messages.empty())
635  {
636  numVLs = buildValidatorListMessage(
637  messages,
638  peerSequence,
639  rawVersion,
640  rawManifest,
641  blobInfos,
642  maxSize);
643  if (messages.empty())
644  // No message was generated. Create an empty placeholder so we
645  // dont' repeat the work later.
646  messages.emplace_back();
647  }
648 
649  // Don't send it next time.
650  return {maxSequence, numVLs};
651  }
652  else if (messageVersion == 1 && peerSequence < currentSeq)
653  {
654  // Version 1
655  if (messages.empty())
656  {
657  numVLs = buildValidatorListMessage(
658  messages,
659  rawVersion,
660  currentBlob.manifest ? *currentBlob.manifest : rawManifest,
661  currentBlob,
662  maxSize);
663  if (messages.empty())
664  // No message was generated. Create an empty placeholder so we
665  // dont' repeat the work later.
666  messages.emplace_back();
667  }
668 
669  // Don't send it next time.
670  return {currentSeq, numVLs};
671  }
672  return {0, 0};
673 }
674 
675 // static
676 void
678  Peer& peer,
679  std::uint64_t peerSequence,
680  PublicKey const& publisherKey,
681  std::size_t maxSequence,
682  std::uint32_t rawVersion,
683  std::string const& rawManifest,
686  HashRouter& hashRouter,
687  beast::Journal j)
688 {
689  std::size_t const messageVersion =
691  ? 2
693  : 0;
694  if (!messageVersion)
695  return;
696  auto const [newPeerSequence, numVLs] = buildValidatorListMessages(
697  messageVersion,
698  peerSequence,
699  maxSequence,
700  rawVersion,
701  rawManifest,
702  blobInfos,
703  messages);
704  if (newPeerSequence)
705  {
706  assert(!messages.empty());
707  // Don't send it next time.
708  peer.setPublisherListSequence(publisherKey, newPeerSequence);
709 
710  bool sent = false;
711  for (auto const& message : messages)
712  {
713  if (message.message)
714  {
715  peer.send(message.message);
716  hashRouter.addSuppressionPeer(message.hash, peer.id());
717  sent = true;
718  }
719  }
720  // The only way sent wil be false is if the messages was too big, and
721  // thus there will only be one entry without a message
722  assert(sent || messages.size() == 1);
723  if (sent)
724  {
725  if (messageVersion > 1)
726  JLOG(j.debug())
727  << "Sent " << messages.size()
728  << " validator list collection(s) containing " << numVLs
729  << " validator list(s) for " << strHex(publisherKey)
730  << " with sequence range " << peerSequence << ", "
731  << newPeerSequence << " to "
732  << peer.getRemoteAddress().to_string() << " [" << peer.id()
733  << "]";
734  else
735  {
736  assert(numVLs == 1);
737  JLOG(j.debug())
738  << "Sent validator list for " << strHex(publisherKey)
739  << " with sequence " << newPeerSequence << " to "
740  << peer.getRemoteAddress().to_string() << " [" << peer.id()
741  << "]";
742  }
743  }
744  }
745 }
746 
747 // static
748 void
750  Peer& peer,
751  std::uint64_t peerSequence,
752  PublicKey const& publisherKey,
753  std::size_t maxSequence,
754  std::uint32_t rawVersion,
755  std::string const& rawManifest,
757  HashRouter& hashRouter,
758  beast::Journal j)
759 {
762  peer,
763  peerSequence,
764  publisherKey,
765  maxSequence,
766  rawVersion,
767  rawManifest,
768  blobInfos,
769  messages,
770  hashRouter,
771  j);
772 }
773 
774 // static
775 void
779 {
780  auto const& current = lists.current;
781  auto const& remaining = lists.remaining;
782  blobInfos[current.sequence] = {
783  current.rawBlob, current.rawSignature, current.rawManifest};
784  for (auto const& [sequence, vl] : remaining)
785  {
786  blobInfos[sequence] = {vl.rawBlob, vl.rawSignature, vl.rawManifest};
787  }
788 }
789 
790 // static
794 {
796  buildBlobInfos(result, lists);
797  return result;
798 }
799 
800 // static
801 void
803  PublicKey const& publisherKey,
805  std::size_t maxSequence,
806  uint256 const& hash,
807  Overlay& overlay,
808  HashRouter& hashRouter,
809  beast::Journal j)
810 {
811  auto const toSkip = hashRouter.shouldRelay(hash);
812 
813  if (toSkip)
814  {
815  // We don't know what messages or message versions we're sending
816  // until we examine our peer's properties. Build the message(s) on
817  // demand, but reuse them when possible.
818 
819  // This will hold a v1 message with only the current VL if we have
820  // any peers that don't support v2
822  // This will hold v2 messages indexed by the peer's
823  // `publisherListSequence`. For each `publisherListSequence`, we'll
824  // only send the VLs with higher sequences.
826  messages2;
827  // If any peers are found that are worth considering, this list will
828  // be built to hold info for all of the valid VLs.
830 
831  assert(
832  lists.current.sequence == maxSequence ||
833  lists.remaining.count(maxSequence) == 1);
834  // Can't use overlay.foreach here because we need to modify
835  // the peer, and foreach provides a const&
836  for (auto& peer : overlay.getActivePeers())
837  {
838  if (toSkip->count(peer->id()) == 0)
839  {
840  auto const peerSequence =
841  peer->publisherListSequence(publisherKey).value_or(0);
842  if (peerSequence < maxSequence)
843  {
844  if (blobInfos.empty())
845  buildBlobInfos(blobInfos, lists);
846  auto const v2 = peer->supportsFeature(
849  *peer,
850  peerSequence,
851  publisherKey,
852  maxSequence,
853  lists.rawVersion,
854  lists.rawManifest,
855  blobInfos,
856  v2 ? messages2[peerSequence] : messages1,
857  hashRouter,
858  j);
859  // Even if the peer doesn't support the messages,
860  // suppress it so it'll be ignored next time.
861  hashRouter.addSuppressionPeer(hash, peer->id());
862  }
863  }
864  }
865  }
866 }
867 
870  std::string const& manifest,
871  std::uint32_t version,
872  std::vector<ValidatorBlobInfo> const& blobs,
873  std::string siteUri,
874  uint256 const& hash,
875  Overlay& overlay,
876  HashRouter& hashRouter,
877  NetworkOPs& networkOPs)
878 {
879  auto const result =
880  applyLists(manifest, version, blobs, std::move(siteUri), hash);
881  auto const disposition = result.bestDisposition();
882 
883  if (disposition == ListDisposition::accepted)
884  {
885  bool good = true;
886  for (auto const& [pubKey, listCollection] : publisherLists_)
887  {
888  (void)pubKey;
889  if (listCollection.status != PublisherStatus::available)
890  {
891  good = false;
892  break;
893  }
894  }
895  if (good)
896  {
897  networkOPs.clearUNLBlocked();
898  }
899  }
900  bool broadcast = disposition <= ListDisposition::known_sequence;
901 
902  if (broadcast)
903  {
904  auto const& pubCollection = publisherLists_[*result.publisherKey];
905  assert(
906  result.status <= PublisherStatus::expired && result.publisherKey &&
907  pubCollection.maxSequence);
909  *result.publisherKey,
910  pubCollection,
911  *pubCollection.maxSequence,
912  hash,
913  overlay,
914  hashRouter,
915  j_);
916  }
917 
918  return result;
919 }
920 
923  std::string const& manifest,
924  std::uint32_t version,
925  std::vector<ValidatorBlobInfo> const& blobs,
926  std::string siteUri,
927  std::optional<uint256> const& hash /* = {} */)
928 {
929  if (std::count(
932  version) != 1)
934 
935  std::lock_guard lock{mutex_};
936 
937  PublisherListStats result;
938  for (auto const& blobInfo : blobs)
939  {
940  auto stats = applyList(
941  manifest,
942  blobInfo.manifest,
943  blobInfo.blob,
944  blobInfo.signature,
945  version,
946  siteUri,
947  hash,
948  lock);
949 
950  if (stats.bestDisposition() < result.bestDisposition() ||
951  (stats.bestDisposition() == result.bestDisposition() &&
952  stats.sequence > result.sequence))
953  {
954  stats.mergeDispositions(result);
955  result = std::move(stats);
956  }
957  else
958  result.mergeDispositions(stats);
960  }
961 
962  // Clean up the collection, because some of the processing may have made it
963  // inconsistent
964  if (result.publisherKey && publisherLists_.count(*result.publisherKey))
965  {
966  auto& pubCollection = publisherLists_[*result.publisherKey];
967  auto& remaining = pubCollection.remaining;
968  auto const& current = pubCollection.current;
969  for (auto iter = remaining.begin(); iter != remaining.end();)
970  {
971  auto next = std::next(iter);
972  assert(next == remaining.end() || next->first > iter->first);
973  if (iter->first <= current.sequence ||
974  (next != remaining.end() &&
975  next->second.validFrom <= iter->second.validFrom))
976  {
977  iter = remaining.erase(iter);
978  }
979  else
980  {
981  iter = next;
982  }
983  }
984 
985  cacheValidatorFile(lock, *result.publisherKey);
986 
987  pubCollection.fullHash = sha512Half(pubCollection);
988 
989  result.sequence = *pubCollection.maxSequence;
990  }
991 
992  return result;
993 }
994 
995 void
997  PublicKey const& pubKey,
998  PublisherList const& current,
999  std::vector<PublicKey> const& oldList,
1001 {
1002  // Update keyListings_ for added and removed keys
1003  std::vector<PublicKey> const& publisherList = current.list;
1004  std::vector<std::string> const& manifests = current.manifests;
1005  auto iNew = publisherList.begin();
1006  auto iOld = oldList.begin();
1007  while (iNew != publisherList.end() || iOld != oldList.end())
1008  {
1009  if (iOld == oldList.end() ||
1010  (iNew != publisherList.end() && *iNew < *iOld))
1011  {
1012  // Increment list count for added keys
1013  ++keyListings_[*iNew];
1014  ++iNew;
1015  }
1016  else if (
1017  iNew == publisherList.end() ||
1018  (iOld != oldList.end() && *iOld < *iNew))
1019  {
1020  // Decrement list count for removed keys
1021  if (keyListings_[*iOld] <= 1)
1022  keyListings_.erase(*iOld);
1023  else
1024  --keyListings_[*iOld];
1025  ++iOld;
1026  }
1027  else
1028  {
1029  ++iNew;
1030  ++iOld;
1031  }
1032  }
1033 
1034  if (publisherList.empty())
1035  {
1036  JLOG(j_.warn()) << "No validator keys included in valid list";
1037  }
1038 
1039  for (auto const& valManifest : manifests)
1040  {
1041  auto m = deserializeManifest(base64_decode(valManifest));
1042 
1043  if (!m || !keyListings_.count(m->masterKey))
1044  {
1045  JLOG(j_.warn()) << "List for " << strHex(pubKey)
1046  << " contained untrusted validator manifest";
1047  continue;
1048  }
1049 
1050  if (auto const r = validatorManifests_.applyManifest(std::move(*m));
1052  {
1053  JLOG(j_.warn()) << "List for " << strHex(pubKey)
1054  << " contained invalid validator manifest";
1055  }
1056  }
1057 }
1058 
1061  std::string const& globalManifest,
1062  std::optional<std::string> const& localManifest,
1063  std::string const& blob,
1064  std::string const& signature,
1065  std::uint32_t version,
1066  std::string siteUri,
1067  std::optional<uint256> const& hash,
1068  ValidatorList::lock_guard const& lock)
1069 {
1070  using namespace std::string_literals;
1071 
1072  Json::Value list;
1073  PublicKey pubKey;
1074  auto const& manifest = localManifest ? *localManifest : globalManifest;
1075  auto const result = verify(lock, list, pubKey, manifest, blob, signature);
1076  if (result > ListDisposition::pending)
1077  {
1078  if (publisherLists_.count(pubKey))
1079  {
1080  auto const& pubCollection = publisherLists_[pubKey];
1081  if (pubCollection.maxSequence &&
1082  (result == ListDisposition::same_sequence ||
1084  {
1085  // We've seen something valid list for this publisher
1086  // already, so return what we know about it.
1087  return PublisherListStats{
1088  result,
1089  pubKey,
1090  pubCollection.status,
1091  *pubCollection.maxSequence};
1092  }
1093  }
1094  return PublisherListStats{result};
1095  }
1096 
1097  // Update publisher's list
1098  auto& pubCollection = publisherLists_[pubKey];
1099  auto const sequence = list[jss::sequence].asUInt();
1100  auto const accepted =
1101  (result == ListDisposition::accepted ||
1102  result == ListDisposition::expired);
1103 
1104  if (accepted)
1105  pubCollection.status = result == ListDisposition::accepted
1108  pubCollection.rawManifest = globalManifest;
1109  if (!pubCollection.maxSequence || sequence > *pubCollection.maxSequence)
1110  pubCollection.maxSequence = sequence;
1111 
1112  Json::Value const& newList = list[jss::validators];
1113  std::vector<PublicKey> oldList;
1114  if (accepted && pubCollection.remaining.count(sequence) != 0)
1115  {
1116  // We've seen this list before and stored it in "remaining". The
1117  // normal expected process is that the processed list would have
1118  // already been moved in to "current" by "updateTrusted()", but race
1119  // conditions are possible, or the node may have lost sync, so do
1120  // some of that work here.
1121  auto& publisher = pubCollection.current;
1122  // Copy the old validator list
1123  oldList = std::move(pubCollection.current.list);
1124  // Move the publisher info from "remaining" to "current"
1125  publisher = std::move(pubCollection.remaining[sequence]);
1126  // Remove the entry in "remaining"
1127  pubCollection.remaining.erase(sequence);
1128  // Done
1129  assert(publisher.sequence == sequence);
1130  }
1131  else
1132  {
1133  auto& publisher = accepted ? pubCollection.current
1134  : pubCollection.remaining[sequence];
1135  publisher.sequence = sequence;
1136  publisher.validFrom = TimeKeeper::time_point{TimeKeeper::duration{
1137  list.isMember(jss::effective) ? list[jss::effective].asUInt() : 0}};
1138  publisher.validUntil = TimeKeeper::time_point{
1139  TimeKeeper::duration{list[jss::expiration].asUInt()}};
1140  publisher.siteUri = std::move(siteUri);
1141  publisher.rawBlob = blob;
1142  publisher.rawSignature = signature;
1143  publisher.rawManifest = localManifest;
1144  if (hash)
1145  publisher.hash = *hash;
1146 
1147  std::vector<PublicKey>& publisherList = publisher.list;
1148  std::vector<std::string>& manifests = publisher.manifests;
1149 
1150  // Copy the old validator list
1151  oldList = std::move(publisherList);
1152  // Build the new validator list from "newList"
1153  publisherList.clear();
1154  publisherList.reserve(newList.size());
1155  for (auto const& val : newList)
1156  {
1157  if (val.isObject() && val.isMember(jss::validation_public_key) &&
1158  val[jss::validation_public_key].isString())
1159  {
1160  std::optional<Blob> const ret =
1161  strUnHex(val[jss::validation_public_key].asString());
1162 
1163  if (!ret || !publicKeyType(makeSlice(*ret)))
1164  {
1165  JLOG(j_.error())
1166  << "Invalid node identity: "
1167  << val[jss::validation_public_key].asString();
1168  }
1169  else
1170  {
1171  publisherList.push_back(
1172  PublicKey(Slice{ret->data(), ret->size()}));
1173  }
1174 
1175  if (val.isMember(jss::manifest) &&
1176  val[jss::manifest].isString())
1177  manifests.push_back(val[jss::manifest].asString());
1178  }
1179  }
1180 
1181  // Standardize the list order by sorting
1182  std::sort(publisherList.begin(), publisherList.end());
1183  }
1184  // If this publisher has ever sent a more updated version than the one
1185  // in this file, keep it. This scenario is unlikely, but legal.
1186  pubCollection.rawVersion = std::max(pubCollection.rawVersion, version);
1187  if (!pubCollection.remaining.empty())
1188  {
1189  // If there are any pending VLs, then this collection must be at least
1190  // version 2.
1191  pubCollection.rawVersion = std::max(pubCollection.rawVersion, 2u);
1192  }
1193 
1194  PublisherListStats const applyResult{
1195  result, pubKey, pubCollection.status, *pubCollection.maxSequence};
1196 
1197  if (accepted)
1198  {
1199  updatePublisherList(pubKey, pubCollection.current, oldList, lock);
1200  }
1201 
1202  return applyResult;
1203 }
1204 
1207 {
1208  using namespace std::string_literals;
1209  using namespace boost::filesystem;
1210  using namespace boost::system::errc;
1211 
1212  std::lock_guard lock{mutex_};
1213 
1215  sites.reserve(publisherLists_.size());
1216  for (auto const& [pubKey, publisherCollection] : publisherLists_)
1217  {
1218  boost::system::error_code ec;
1219 
1220  if (publisherCollection.status == PublisherStatus::available)
1221  continue;
1222 
1223  boost::filesystem::path const filename = getCacheFileName(lock, pubKey);
1224 
1225  auto const fullPath{canonical(filename, ec)};
1226  if (ec)
1227  continue;
1228 
1229  auto size = file_size(fullPath, ec);
1230  if (!ec && !size)
1231  {
1232  // Treat an empty file as a missing file, because
1233  // nobody else is going to write it.
1234  ec = make_error_code(no_such_file_or_directory);
1235  }
1236  if (ec)
1237  continue;
1238 
1239  std::string const prefix = [&fullPath]() {
1240 #if _MSC_VER // MSVC: Windows paths need a leading / added
1241  {
1242  return fullPath.root_path() == "/"s ? "file://" : "file:///";
1243  }
1244 #else
1245  {
1246  (void)fullPath;
1247  return "file://";
1248  }
1249 #endif
1250  }();
1251  sites.emplace_back(prefix + fullPath.string());
1252  }
1253 
1254  // Then let the ValidatorSites do the rest of the work.
1255  return sites;
1256 }
1257 
1260  ValidatorList::lock_guard const& lock,
1261  Json::Value& list,
1262  PublicKey& pubKey,
1263  std::string const& manifest,
1264  std::string const& blob,
1265  std::string const& signature)
1266 {
1268 
1269  if (!m || !publisherLists_.count(m->masterKey))
1271 
1272  pubKey = m->masterKey;
1273  auto const revoked = m->revoked();
1274 
1275  auto const result = publisherManifests_.applyManifest(std::move(*m));
1276 
1277  if (revoked && result == ManifestDisposition::accepted)
1278  {
1280  // If the manifest is revoked, no future list is valid either
1281  publisherLists_[pubKey].remaining.clear();
1282  }
1283 
1284  if (revoked || result == ManifestDisposition::invalid)
1286 
1287  auto const sig = strUnHex(signature);
1288  auto const data = base64_decode(blob);
1289  if (!sig ||
1290  !ripple::verify(
1292  makeSlice(data),
1293  makeSlice(*sig)))
1294  return ListDisposition::invalid;
1295 
1296  Json::Reader r;
1297  if (!r.parse(data, list))
1298  return ListDisposition::invalid;
1299 
1300  if (list.isMember(jss::sequence) && list[jss::sequence].isInt() &&
1301  list.isMember(jss::expiration) && list[jss::expiration].isInt() &&
1302  (!list.isMember(jss::effective) || list[jss::effective].isInt()) &&
1303  list.isMember(jss::validators) && list[jss::validators].isArray())
1304  {
1305  auto const sequence = list[jss::sequence].asUInt();
1306  auto const validFrom = TimeKeeper::time_point{TimeKeeper::duration{
1307  list.isMember(jss::effective) ? list[jss::effective].asUInt() : 0}};
1308  auto const validUntil = TimeKeeper::time_point{
1309  TimeKeeper::duration{list[jss::expiration].asUInt()}};
1310  auto const now = timeKeeper_.now();
1311  auto const& listCollection = publisherLists_[pubKey];
1312  if (validUntil <= validFrom)
1313  return ListDisposition::invalid;
1314  else if (sequence < listCollection.current.sequence)
1315  return ListDisposition::stale;
1316  else if (sequence == listCollection.current.sequence)
1318  else if (validUntil <= now)
1319  return ListDisposition::expired;
1320  else if (validFrom > now)
1321  // Not yet valid. Return pending if one of the following is true
1322  // * There's no maxSequence, indicating this is the first blob seen
1323  // for this publisher
1324  // * The sequence is larger than the maxSequence, indicating this
1325  // blob is new
1326  // * There's no entry for this sequence AND this blob is valid
1327  // before the last blob, indicating blobs may be processing out of
1328  // order. This may result in some duplicated processing, but
1329  // prevents the risk of missing valid data. Else return
1330  // known_sequence
1331  return !listCollection.maxSequence ||
1332  sequence > *listCollection.maxSequence ||
1333  (listCollection.remaining.count(sequence) == 0 &&
1334  validFrom < listCollection.remaining
1335  .at(*listCollection.maxSequence)
1336  .validFrom)
1339  }
1340  else
1341  {
1342  return ListDisposition::invalid;
1343  }
1344 
1346 }
1347 
1348 bool
1349 ValidatorList::listed(PublicKey const& identity) const
1350 {
1351  std::shared_lock read_lock{mutex_};
1352 
1353  auto const pubKey = validatorManifests_.getMasterKey(identity);
1354  return keyListings_.find(pubKey) != keyListings_.end();
1355 }
1356 
1357 bool
1360  PublicKey const& identity) const
1361 {
1362  auto const pubKey = validatorManifests_.getMasterKey(identity);
1363  return trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end();
1364 }
1365 
1366 bool
1367 ValidatorList::trusted(PublicKey const& identity) const
1368 {
1369  std::shared_lock read_lock{mutex_};
1370  return trusted(read_lock, identity);
1371 }
1372 
1375 {
1376  std::shared_lock read_lock{mutex_};
1377 
1378  auto const pubKey = validatorManifests_.getMasterKey(identity);
1379  if (keyListings_.find(pubKey) != keyListings_.end())
1380  return pubKey;
1381  return std::nullopt;
1382 }
1383 
1387  PublicKey const& identity) const
1388 {
1389  auto const pubKey = validatorManifests_.getMasterKey(identity);
1390  if (trustedMasterKeys_.find(pubKey) != trustedMasterKeys_.end())
1391  return pubKey;
1392  return std::nullopt;
1393 }
1394 
1397 {
1398  std::shared_lock read_lock{mutex_};
1399 
1400  return getTrustedKey(read_lock, identity);
1401 }
1402 
1403 bool
1405 {
1406  std::shared_lock read_lock{mutex_};
1407  return identity.size() && publisherLists_.count(identity) &&
1408  publisherLists_.at(identity).status < PublisherStatus::revoked;
1409 }
1410 
1411 PublicKey
1413 {
1414  std::shared_lock read_lock{mutex_};
1415  return localPubKey_;
1416 }
1417 
1418 bool
1421  PublicKey const& publisherKey,
1422  PublisherStatus reason)
1423 {
1424  assert(
1425  reason != PublisherStatus::available &&
1426  reason != PublisherStatus::unavailable);
1427  auto const iList = publisherLists_.find(publisherKey);
1428  if (iList == publisherLists_.end())
1429  return false;
1430 
1431  JLOG(j_.debug()) << "Removing validator list for publisher "
1432  << strHex(publisherKey);
1433 
1434  for (auto const& val : iList->second.current.list)
1435  {
1436  auto const& iVal = keyListings_.find(val);
1437  if (iVal == keyListings_.end())
1438  continue;
1439 
1440  if (iVal->second <= 1)
1441  keyListings_.erase(iVal);
1442  else
1443  --iVal->second;
1444  }
1445 
1446  iList->second.current.list.clear();
1447  iList->second.status = reason;
1448 
1449  return true;
1450 }
1451 
1454 {
1455  return publisherLists_.size();
1456 }
1457 
1460 {
1461  std::shared_lock read_lock{mutex_};
1462  return count(read_lock);
1463 }
1464 
1467 {
1469  for (auto const& [pubKey, collection] : publisherLists_)
1470  {
1471  (void)pubKey;
1472  // Unfetched
1473  auto const& current = collection.current;
1474  if (current.validUntil == TimeKeeper::time_point{})
1475  return std::nullopt;
1476 
1477  // Find the latest validUntil in a chain where the next validFrom
1478  // overlaps with the previous validUntil. applyLists has already cleaned
1479  // up the list so the validFrom dates are guaranteed increasing.
1480  auto chainedExpiration = current.validUntil;
1481  for (auto const& [sequence, check] : collection.remaining)
1482  {
1483  (void)sequence;
1484  if (check.validFrom <= chainedExpiration)
1485  chainedExpiration = check.validUntil;
1486  else
1487  break;
1488  }
1489 
1490  // Earliest
1491  if (!res || chainedExpiration < *res)
1492  {
1493  res = chainedExpiration;
1494  }
1495  }
1496  return res;
1497 }
1498 
1501 {
1502  std::shared_lock read_lock{mutex_};
1503  return expires(read_lock);
1504 }
1505 
1508 {
1510 
1511  std::shared_lock read_lock{mutex_};
1512 
1513  res[jss::validation_quorum] = static_cast<Json::UInt>(quorum_);
1514 
1515  {
1516  auto& x = (res[jss::validator_list] = Json::objectValue);
1517 
1518  x[jss::count] = static_cast<Json::UInt>(count(read_lock));
1519 
1520  if (auto when = expires(read_lock))
1521  {
1522  if (*when == TimeKeeper::time_point::max())
1523  {
1524  x[jss::expiration] = "never";
1525  x[jss::status] = "active";
1526  }
1527  else
1528  {
1529  x[jss::expiration] = to_string(*when);
1530 
1531  if (*when > timeKeeper_.now())
1532  x[jss::status] = "active";
1533  else
1534  x[jss::status] = "expired";
1535  }
1536  }
1537  else
1538  {
1539  x[jss::status] = "unknown";
1540  x[jss::expiration] = "unknown";
1541  }
1542  }
1543 
1544  // Local static keys
1545  PublicKey local;
1546  Json::Value& jLocalStaticKeys =
1547  (res[jss::local_static_keys] = Json::arrayValue);
1548  if (auto it = publisherLists_.find(local); it != publisherLists_.end())
1549  {
1550  for (auto const& key : it->second.current.list)
1551  jLocalStaticKeys.append(toBase58(TokenType::NodePublic, key));
1552  }
1553 
1554  // Publisher lists
1555  Json::Value& jPublisherLists =
1556  (res[jss::publisher_lists] = Json::arrayValue);
1557  for (auto const& [publicKey, pubCollection] : publisherLists_)
1558  {
1559  if (local == publicKey)
1560  continue;
1561  Json::Value& curr = jPublisherLists.append(Json::objectValue);
1562  curr[jss::pubkey_publisher] = strHex(publicKey);
1563  curr[jss::available] =
1564  pubCollection.status == PublisherStatus::available;
1565 
1566  auto appendList = [](PublisherList const& publisherList,
1567  Json::Value& target) {
1568  target[jss::uri] = publisherList.siteUri;
1569  if (publisherList.validUntil != TimeKeeper::time_point{})
1570  {
1571  target[jss::seq] =
1572  static_cast<Json::UInt>(publisherList.sequence);
1573  target[jss::expiration] = to_string(publisherList.validUntil);
1574  }
1575  if (publisherList.validFrom != TimeKeeper::time_point{})
1576  target[jss::effective] = to_string(publisherList.validFrom);
1577  Json::Value& keys = (target[jss::list] = Json::arrayValue);
1578  for (auto const& key : publisherList.list)
1579  {
1581  }
1582  };
1583  {
1584  auto const& current = pubCollection.current;
1585  appendList(current, curr);
1586  if (current.validUntil != TimeKeeper::time_point{})
1587  {
1588  curr[jss::version] = pubCollection.rawVersion;
1589  }
1590  }
1591 
1592  Json::Value remaining(Json::arrayValue);
1593  for (auto const& [sequence, future] : pubCollection.remaining)
1594  {
1595  using namespace std::chrono_literals;
1596 
1597  (void)sequence;
1598  Json::Value& r = remaining.append(Json::objectValue);
1599  appendList(future, r);
1600  // Race conditions can happen, so make this check "fuzzy"
1601  assert(future.validFrom > timeKeeper_.now() + 600s);
1602  }
1603  if (remaining.size())
1604  curr[jss::remaining] = std::move(remaining);
1605  }
1606 
1607  // Trusted validator keys
1608  Json::Value& jValidatorKeys =
1609  (res[jss::trusted_validator_keys] = Json::arrayValue);
1610  for (auto const& k : trustedMasterKeys_)
1611  {
1612  jValidatorKeys.append(toBase58(TokenType::NodePublic, k));
1613  }
1614 
1615  // signing keys
1616  Json::Value& jSigningKeys = (res[jss::signing_keys] = Json::objectValue);
1617  validatorManifests_.for_each_manifest([&jSigningKeys,
1618  this](Manifest const& manifest) {
1619  auto it = keyListings_.find(manifest.masterKey);
1620  if (it != keyListings_.end())
1621  {
1622  jSigningKeys[toBase58(TokenType::NodePublic, manifest.masterKey)] =
1623  toBase58(TokenType::NodePublic, manifest.signingKey);
1624  }
1625  });
1626 
1627  // Negative UNL
1628  if (!negativeUNL_.empty())
1629  {
1630  Json::Value& jNegativeUNL = (res[jss::NegativeUNL] = Json::arrayValue);
1631  for (auto const& k : negativeUNL_)
1632  {
1633  jNegativeUNL.append(toBase58(TokenType::NodePublic, k));
1634  }
1635  }
1636 
1637  return res;
1638 }
1639 
1640 void
1642  std::function<void(PublicKey const&, bool)> func) const
1643 {
1644  std::shared_lock read_lock{mutex_};
1645 
1646  for (auto const& v : keyListings_)
1647  func(v.first, trusted(read_lock, v.first));
1648 }
1649 
1650 void
1652  std::function<void(
1653  std::string const& manifest,
1654  std::uint32_t version,
1656  PublicKey const& pubKey,
1657  std::size_t maxSequence,
1658  uint256 const& hash)> func) const
1659 {
1660  std::shared_lock read_lock{mutex_};
1661 
1662  for (auto const& [key, plCollection] : publisherLists_)
1663  {
1664  if (plCollection.status != PublisherStatus::available || key.empty())
1665  continue;
1666  assert(plCollection.maxSequence);
1667  func(
1668  plCollection.rawManifest,
1669  plCollection.rawVersion,
1670  buildBlobInfos(plCollection),
1671  key,
1672  plCollection.maxSequence.value_or(0),
1673  plCollection.fullHash);
1674  }
1675 }
1676 
1679  boost::beast::string_view const& pubKey,
1680  std::optional<std::uint32_t> forceVersion /* = {} */)
1681 {
1682  std::shared_lock read_lock{mutex_};
1683 
1684  auto const keyBlob = strViewUnHex(pubKey);
1685 
1686  if (!keyBlob || !publicKeyType(makeSlice(*keyBlob)))
1687  {
1688  JLOG(j_.info()) << "Invalid requested validator list publisher key: "
1689  << pubKey;
1690  return {};
1691  }
1692 
1693  auto id = PublicKey(makeSlice(*keyBlob));
1694 
1695  auto const iter = publisherLists_.find(id);
1696 
1697  if (iter == publisherLists_.end() ||
1698  iter->second.status != PublisherStatus::available)
1699  return {};
1700 
1701  Json::Value value =
1702  buildFileData(std::string{pubKey}, iter->second, forceVersion, j_);
1703 
1704  return value;
1705 }
1706 
1709  std::size_t unlSize,
1710  std::size_t effectiveUnlSize,
1711  std::size_t seenSize)
1712 {
1713  // Use quorum if specified via command line.
1714  if (minimumQuorum_ > 0)
1715  {
1716  JLOG(j_.warn()) << "Using potentially unsafe quorum of "
1717  << *minimumQuorum_
1718  << " as specified on the command line";
1719  return *minimumQuorum_;
1720  }
1721 
1722  // Do not use achievable quorum until lists from all configured
1723  // publishers are available
1724  for (auto const& list : publisherLists_)
1725  {
1726  if (list.second.status != PublisherStatus::available)
1728  }
1729 
1730  // Use an 80% quorum to balance fork safety, liveness, and required UNL
1731  // overlap.
1732  //
1733  // Theorem 8 of the Analysis of the XRP Ledger Consensus Protocol
1734  // (https://arxiv.org/abs/1802.07242) says:
1735  // XRP LCP guarantees fork safety if Oi,j > nj/2 + ni − qi + ti,j
1736  // for every pair of nodes Pi, Pj.
1737  //
1738  // ni: size of Pi's UNL
1739  // nj: size of Pj's UNL
1740  // Oi,j: number of validators in both UNLs
1741  // qi: validation quorum for Pi's UNL
1742  // ti, tj: maximum number of allowed Byzantine faults in Pi and Pj's
1743  // UNLs ti,j: min{ti, tj, Oi,j}
1744  //
1745  // Assume ni < nj, meaning and ti,j = ti
1746  //
1747  // For qi = .8*ni, we make ti <= .2*ni
1748  // (We could make ti lower and tolerate less UNL overlap. However in
1749  // order to prioritize safety over liveness, we need ti >= ni - qi)
1750  //
1751  // An 80% quorum allows two UNLs to safely have < .2*ni unique
1752  // validators between them:
1753  //
1754  // pi = ni - Oi,j
1755  // pj = nj - Oi,j
1756  //
1757  // Oi,j > nj/2 + ni − qi + ti,j
1758  // ni - pi > (ni - pi + pj)/2 + ni − .8*ni + .2*ni
1759  // pi + pj < .2*ni
1760  //
1761  // Note that the negative UNL protocol introduced the
1762  // AbsoluteMinimumQuorum which is 60% of the original UNL size. The
1763  // effective quorum should not be lower than it.
1764  return static_cast<std::size_t>(std::max(
1765  std::ceil(effectiveUnlSize * 0.8f), std::ceil(unlSize * 0.6f)));
1766 }
1767 
1770  hash_set<NodeID> const& seenValidators,
1771  NetClock::time_point closeTime,
1772  NetworkOPs& ops,
1773  Overlay& overlay,
1774  HashRouter& hashRouter)
1775 {
1776  using namespace std::chrono_literals;
1777  if (timeKeeper_.now() > closeTime + 30s)
1778  closeTime = timeKeeper_.now();
1779 
1780  std::lock_guard lock{mutex_};
1781 
1782  // Rotate pending and remove expired published lists
1783  bool good = true;
1784  for (auto& [pubKey, collection] : publisherLists_)
1785  {
1786  {
1787  auto& remaining = collection.remaining;
1788  auto const firstIter = remaining.begin();
1789  auto iter = firstIter;
1790  if (iter != remaining.end() && iter->second.validFrom <= closeTime)
1791  {
1792  // Find the LAST candidate that is ready to go live.
1793  for (auto next = std::next(iter); next != remaining.end() &&
1794  next->second.validFrom <= closeTime;
1795  ++iter, ++next)
1796  {
1797  assert(std::next(iter) == next);
1798  }
1799  assert(iter != remaining.end());
1800 
1801  // Rotate the pending list in to current
1802  auto sequence = iter->first;
1803  auto& candidate = iter->second;
1804  auto& current = collection.current;
1805  assert(candidate.validFrom <= closeTime);
1806 
1807  auto const oldList = current.list;
1808  current = std::move(candidate);
1809  if (collection.status != PublisherStatus::available)
1810  collection.status = PublisherStatus::available;
1811  assert(current.sequence == sequence);
1812  // If the list is expired, remove the validators so they don't
1813  // get processed in. The expiration check below will do the rest
1814  // of the work
1815  if (current.validUntil <= closeTime)
1816  current.list.clear();
1817 
1818  updatePublisherList(pubKey, current, oldList, lock);
1819 
1820  // Only broadcast the current, which will consequently only
1821  // send to peers that don't understand v2, or which are
1822  // unknown (unlikely). Those that do understand v2 should
1823  // already have this list and are in the process of
1824  // switching themselves.
1826  pubKey,
1827  collection,
1828  sequence,
1829  current.hash,
1830  overlay,
1831  hashRouter,
1832  j_);
1833 
1834  // Erase any candidates that we skipped over, plus this one
1835  remaining.erase(firstIter, std::next(iter));
1836  }
1837  }
1838  // Remove if expired
1839  if (collection.status == PublisherStatus::available &&
1840  collection.current.validUntil <= closeTime)
1841  {
1843  ops.setUNLBlocked();
1844  }
1845  if (collection.status != PublisherStatus::available)
1846  good = false;
1847  }
1848  if (good)
1849  ops.clearUNLBlocked();
1850 
1851  TrustChanges trustChanges;
1852 
1853  auto it = trustedMasterKeys_.cbegin();
1854  while (it != trustedMasterKeys_.cend())
1855  {
1856  if (!keyListings_.count(*it) || validatorManifests_.revoked(*it))
1857  {
1858  trustChanges.removed.insert(calcNodeID(*it));
1859  it = trustedMasterKeys_.erase(it);
1860  }
1861  else
1862  {
1863  ++it;
1864  }
1865  }
1866 
1867  for (auto const& val : keyListings_)
1868  {
1869  if (!validatorManifests_.revoked(val.first) &&
1870  trustedMasterKeys_.emplace(val.first).second)
1871  trustChanges.added.insert(calcNodeID(val.first));
1872  }
1873 
1874  // If there were any changes, we need to update the ephemeral signing
1875  // keys:
1876  if (!trustChanges.added.empty() || !trustChanges.removed.empty())
1877  {
1878  trustedSigningKeys_.clear();
1879 
1880  for (auto const& k : trustedMasterKeys_)
1882  }
1883 
1884  JLOG(j_.debug())
1885  << trustedMasterKeys_.size() << " of " << keyListings_.size()
1886  << " listed validators eligible for inclusion in the trusted set";
1887 
1888  auto const unlSize = trustedMasterKeys_.size();
1889  auto effectiveUnlSize = unlSize;
1890  auto seenSize = seenValidators.size();
1891  if (!negativeUNL_.empty())
1892  {
1893  for (auto const& k : trustedMasterKeys_)
1894  {
1895  if (negativeUNL_.count(k))
1896  --effectiveUnlSize;
1897  }
1898  hash_set<NodeID> negUnlNodeIDs;
1899  for (auto const& k : negativeUNL_)
1900  {
1901  negUnlNodeIDs.emplace(calcNodeID(k));
1902  }
1903  for (auto const& nid : seenValidators)
1904  {
1905  if (negUnlNodeIDs.count(nid))
1906  --seenSize;
1907  }
1908  }
1909  quorum_ = calculateQuorum(unlSize, effectiveUnlSize, seenSize);
1910 
1911  JLOG(j_.debug()) << "Using quorum of " << quorum_ << " for new set of "
1912  << unlSize << " trusted validators ("
1913  << trustChanges.added.size() << " added, "
1914  << trustChanges.removed.size() << " removed)";
1915 
1916  if (unlSize < quorum_)
1917  {
1918  JLOG(j_.warn()) << "New quorum of " << quorum_
1919  << " exceeds the number of trusted validators ("
1920  << unlSize << ")";
1921  }
1922 
1923  if (publisherLists_.size() && unlSize == 0)
1924  {
1925  // No validators. Lock down.
1926  ops.setUNLBlocked();
1927  }
1928 
1929  return trustChanges;
1930 }
1931 
1934 {
1935  std::shared_lock read_lock{mutex_};
1936  return trustedMasterKeys_;
1937 }
1938 
1941 {
1942  std::shared_lock read_lock{mutex_};
1943  return negativeUNL_;
1944 }
1945 
1946 void
1948 {
1949  std::lock_guard lock{mutex_};
1950  negativeUNL_ = negUnl;
1951 }
1952 
1955  std::vector<std::shared_ptr<STValidation>>&& validations) const
1956 {
1957  // Remove validations that are from validators on the negative UNL.
1958  auto ret = std::move(validations);
1959 
1960  std::shared_lock read_lock{mutex_};
1961  if (!negativeUNL_.empty())
1962  {
1963  ret.erase(
1965  ret.begin(),
1966  ret.end(),
1967  [&](auto const& v) -> bool {
1968  if (auto const masterKey =
1969  getTrustedKey(read_lock, v->getSignerPublic());
1970  masterKey)
1971  {
1972  return negativeUNL_.count(*masterKey);
1973  }
1974  else
1975  {
1976  return false;
1977  }
1978  }),
1979  ret.end());
1980  }
1981 
1982  return ret;
1983 }
1984 
1985 } // namespace ripple
Json::Value::isInt
bool isInt() const
Definition: json_value.cpp:979
ripple::NetworkOPs
Provides server functionality for clients.
Definition: NetworkOPs.h:86
ripple::ValidatorList::MessageWithHash::MessageWithHash
MessageWithHash()=default
ripple::ValidatorList::PublisherList::sequence
std::size_t sequence
Definition: ValidatorList.h:180
ripple::ValidatorList::validatorManifests_
ManifestCache & validatorManifests_
Definition: ValidatorList.h:224
ripple::ListDisposition::pending
@ pending
List will be valid in the future.
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:241
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:1641
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:1412
ripple::ValidatorBlobInfo::signature
std::string signature
Definition: ValidatorList.h:121
ripple::ValidatorList::PublisherListStats::PublisherListStats
PublisherListStats()=default
std::string
STL class.
std::shared_ptr< Message >
ripple::ListDisposition
ListDisposition
Definition: ValidatorList.h:55
ripple::ManifestCache::getMasterKey
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
Definition: app/misc/impl/Manifest.cpp:303
ripple::maximiumMessageSize
constexpr std::size_t maximiumMessageSize
Definition: overlay/Message.h:38
Json::Value::isString
bool isString() const
Definition: json_value.cpp:1009
ripple::calcNodeID
NodeID calcNodeID(PublicKey const &pk)
Calculate the 160-bit node ID from a node public key.
Definition: PublicKey.cpp:303
ripple::Message::totalSize
static std::size_t totalSize(::google::protobuf::Message const &message)
Definition: Message.cpp:62
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::TrustChanges
Changes in trusted nodes after updating validator list.
Definition: ValidatorList.h:107
ripple::ListDisposition::stale
@ stale
Trusted publisher key, but seq is too old.
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
std::unordered_set
STL class.
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
beast::IP::Endpoint::to_string
std::string to_string() const
Returns a string representing the endpoint.
Definition: IPEndpoint.cpp:57
std::pair
ripple::ValidatorList::supportedListVersions
static constexpr std::uint32_t supportedListVersions[]
Definition: ValidatorList.h:256
std::vector::reserve
T reserve(T... args)
ripple::HashPrefix::manifest
@ manifest
Manifest.
Json::UInt
unsigned int UInt
Definition: json_forwards.h:27
ripple::verify
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical) noexcept
Verify a signature on a message.
Definition: PublicKey.cpp:272
ripple::ValidatorList::buildBlobInfos
static void buildBlobInfos(std::map< std::size_t, ValidatorBlobInfo > &blobInfos, PublisherListCollection const &lists)
Definition: ValidatorList.cpp:776
std::vector< std::string >
ripple::ValidatorList::applyListsAndBroadcast
PublisherListStats applyListsAndBroadcast(std::string const &manifest, std::uint32_t version, std::vector< ValidatorBlobInfo > const &blobs, std::string siteUri, uint256 const &hash, Overlay &overlay, HashRouter &hashRouter, NetworkOPs &networkOPs)
Apply multiple published lists of public keys, then broadcast it to all peers that have not seen it o...
Definition: ValidatorList.cpp:869
std::vector::size
T size(T... args)
ripple::ValidatorList::trustedPublisher
bool trustedPublisher(PublicKey const &identity) const
Returns true if public key is a trusted publisher.
Definition: ValidatorList.cpp:1404
ripple::ValidatorList::setNegativeUNL
void setNegativeUNL(hash_set< PublicKey > const &negUnl)
set the Negative UNL with validators' master public keys
Definition: ValidatorList.cpp:1947
ripple::ValidatorList::PublisherListStats
Describes the result of processing a Validator List (UNL), including some of the information from the...
Definition: ValidatorList.h:278
ripple::ValidatorList::negativeUNL_
hash_set< PublicKey > negativeUNL_
Definition: ValidatorList.h:253
ripple::ValidatorList::filePrefix_
static const std::string filePrefix_
Definition: ValidatorList.h:261
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
ripple::ValidatorList::j_
const beast::Journal j_
Definition: ValidatorList.h:228
ripple::ValidatorList::PublisherList::validUntil
TimeKeeper::time_point validUntil
Definition: ValidatorList.h:182
ripple::ValidatorList::getTrustedMasterKeys
hash_set< PublicKey > getTrustedMasterKeys() const
get the trusted master public keys
Definition: ValidatorList.cpp:1933
std::optional::emplace
T emplace(T... args)
ripple::ValidatorList::buildValidatorListMessages
static std::pair< std::size_t, std::size_t > buildValidatorListMessages(std::size_t messageVersion, std::uint64_t peerSequence, std::size_t maxSequence, std::uint32_t rawVersion, std::string const &rawManifest, std::map< std::size_t, ValidatorBlobInfo > const &blobInfos, std::vector< MessageWithHash > &messages, std::size_t maxSize=maximiumMessageSize)
Definition: ValidatorList.cpp:612
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::lock_guard
STL class.
ripple::Peer::send
virtual void send(std::shared_ptr< Message > const &m)=0
ripple::strViewUnHex
std::optional< Blob > strViewUnHex(boost::string_view const &strSrc)
Definition: StringUtilities.h:111
ripple::ValidatorList::maxSupportedBlobs
static constexpr std::size_t maxSupportedBlobs
Definition: ValidatorList.h:259
ripple::ValidatorBlobInfo::blob
std::string blob
Definition: ValidatorList.h:119
ripple::ValidatorList::PublisherListStats::bestDisposition
ListDisposition bestDisposition() const
Definition: ValidatorList.cpp:85
std::vector::back
T back(T... args)
std::function
Json::Value::toStyledString
std::string toStyledString() const
Definition: json_value.cpp:1039
ripple::ValidatorList::parseBlobs
static std::vector< ValidatorBlobInfo > parseBlobs(std::uint32_t version, Json::Value const &body)
Pull the blob/signature/manifest information out of the appropriate Json body fields depending on the...
Definition: ValidatorList.cpp:362
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
cmath
ripple::ListDisposition::expired
@ expired
List is expired, but has the largest non-pending sequence seen so far.
std::sort
T sort(T... args)
std::optional::reset
T reset(T... args)
ripple::ValidatorList::PublisherListCollection
Definition: ValidatorList.h:194
ripple::HashRouter
Routing table for objects identified by hash.
Definition: HashRouter.h:53
std::vector::clear
T clear(T... args)
ripple::ValidatorList::getTrustedKey
std::optional< PublicKey > getTrustedKey(PublicKey const &identity) const
Returns master public key if public key is trusted.
Definition: ValidatorList.cpp:1396
ripple::ValidatorList::sendValidatorList
static void sendValidatorList(Peer &peer, std::uint64_t peerSequence, PublicKey const &publisherKey, std::size_t maxSequence, std::uint32_t rawVersion, std::string const &rawManifest, std::map< std::size_t, ValidatorBlobInfo > const &blobInfos, HashRouter &hashRouter, beast::Journal j)
Definition: ValidatorList.cpp:749
std::vector::push_back
T push_back(T... args)
ripple::publicKeyType
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
ripple::base_uint< 256 >
ripple::ValidatorList::removePublisherList
bool removePublisherList(lock_guard const &, PublicKey const &publisherKey, PublisherStatus reason)
Stop trusting publisher's list of keys.
Definition: ValidatorList.cpp:1419
ripple::ValidatorList::MessageWithHash
Definition: ValidatorList.h:303
ripple::NetworkOPs::clearUNLBlocked
virtual void clearUNLBlocked()=0
ripple::ValidatorList::cacheValidatorFile
void cacheValidatorFile(lock_guard const &lock, PublicKey const &pubKey) const
Write a JSON UNL to a cache file.
Definition: ValidatorList.cpp:331
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::ValidatorList::ValidatorList
ValidatorList(ManifestCache &validatorManifests, ManifestCache &publisherManifests, TimeKeeper &timeKeeper, std::string const &databasePath, beast::Journal j, std::optional< std::size_t > minimumQuorum=std::nullopt)
Definition: ValidatorList.cpp:118
ripple::ValidatorList::dataPath_
const boost::filesystem::path dataPath_
Definition: ValidatorList.h:227
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::ValidatorList::getCacheFileName
boost::filesystem::path getCacheFileName(lock_guard const &, PublicKey const &pubKey) const
Get the filename used for caching UNLs.
Definition: ValidatorList.cpp:250
ripple::ValidatorList::verify
ListDisposition verify(lock_guard const &, 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:1259
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::ValidatorList::PublisherListStats::mergeDispositions
void mergeDispositions(PublisherListStats const &src)
Definition: ValidatorList.cpp:99
ripple::PublicKey::size
std::size_t size() const noexcept
Definition: PublicKey.h:87
ripple::ValidatorList::PublisherList::list
std::vector< PublicKey > list
Definition: ValidatorList.h:178
ripple::Peer::setPublisherListSequence
virtual void setPublisherListSequence(PublicKey const &, std::size_t const)=0
ripple::ValidatorList::getListedKey
std::optional< PublicKey > getListedKey(PublicKey const &identity) const
Returns listed master public if public key is included on any lists.
Definition: ValidatorList.cpp:1374
ripple::ValidatorBlobInfo
Used to represent the information stored in the blobs_v2 Json array.
Definition: ValidatorList.h:116
ripple::TrustChanges::removed
hash_set< NodeID > removed
Definition: ValidatorList.h:112
ripple::ValidatorList::updateTrusted
TrustChanges updateTrusted(hash_set< NodeID > const &seenValidators, NetClock::time_point closeTime, NetworkOPs &ops, Overlay &overlay, HashRouter &hashRouter)
Update trusted nodes.
Definition: ValidatorList.cpp:1769
ripple::ValidatorList::PublisherListCollection::remaining
std::map< std::size_t, PublisherList > remaining
Definition: ValidatorList.h:216
ripple::ValidatorList::publisherManifests_
ManifestCache & publisherManifests_
Definition: ValidatorList.h:225
ripple::ValidatorList::broadcastBlobs
static void broadcastBlobs(PublicKey const &publisherKey, PublisherListCollection const &lists, std::size_t maxSequence, uint256 const &hash, Overlay &overlay, HashRouter &hashRouter, beast::Journal j)
Definition: ValidatorList.cpp:802
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:351
ripple::ValidatorList::timeKeeper_
TimeKeeper & timeKeeper_
Definition: ValidatorList.h:226
ripple::ValidatorList::listed
bool listed(PublicKey const &identity) const
Returns true if public key is included on any lists.
Definition: ValidatorList.cpp:1349
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
std::chrono::time_point
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
ripple::ValidatorList::getAvailable
std::optional< Json::Value > getAvailable(boost::beast::string_view const &pubKey, std::optional< std::uint32_t > forceVersion={})
Returns the current valid list for the given publisher key, if available, as a Json object.
Definition: ValidatorList.cpp:1678
ripple::ValidatorList::PublisherList::siteUri
std::string siteUri
Definition: ValidatorList.h:183
ripple::ManifestCache::for_each_manifest
void for_each_manifest(Function &&f) const
Invokes the callback once for every populated manifest.
Definition: Manifest.h:401
ripple::Overlay::getActivePeers
virtual PeerSequence getActivePeers() const =0
Returns a sequence representing the current list of peers.
ripple::ValStatus::current
@ current
This was a new validation and was added.
ripple::ListDisposition::untrusted
@ untrusted
List signed by untrusted publisher key.
std::accumulate
T accumulate(T... args)
ripple::ManifestCache::getSigningKey
PublicKey getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
Definition: app/misc/impl/Manifest.cpp:291
ripple::Peer::supportsFeature
virtual bool supportsFeature(ProtocolFeature f) const =0
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
std::uint32_t
ripple::ValidatorList::PublisherListStats::sequence
std::size_t sequence
Definition: ValidatorList.h:300
ripple::NetworkOPs::setUNLBlocked
virtual void setUNLBlocked()=0
ripple::HashRouter::shouldRelay
std::optional< std::set< PeerShortID > > shouldRelay(uint256 const &key)
Determines whether the hashed item should be relayed.
Definition: HashRouter.cpp:118
std::remove_if
T remove_if(T... args)
ripple::TimeKeeper
Manages various times used by the server.
Definition: TimeKeeper.h:32
std::map
STL class.
ripple::ListDisposition::unsupported_version
@ unsupported_version
List version is not supported.
ripple::ValidatorList::buildFileData
static Json::Value buildFileData(std::string const &pubKey, PublisherListCollection const &pubCollection, beast::Journal j)
Build a Json representation of the collection, suitable for writing to a cache file,...
Definition: ValidatorList.cpp:259
std::ceil
T ceil(T... args)
ripple::ManifestDisposition::invalid
@ invalid
Timely, but invalid signature.
ripple::Peer::getRemoteAddress
virtual beast::IP::Endpoint getRemoteAddress() const =0
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:1954
ripple::ValidatorList::expires
std::optional< TimeKeeper::time_point > expires() const
Return the time when the validator list will expire.
Definition: ValidatorList.cpp:1500
Json::Value::isArray
bool isArray() const
Definition: json_value.cpp:1015
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:1708
ripple::ManifestDisposition::accepted
@ accepted
Manifest is valid.
ripple::ListDisposition::known_sequence
@ known_sequence
Future sequence already seen.
ripple::ValidatorList::localPubKey_
PublicKey localPubKey_
Definition: ValidatorList.h:250
ripple::ValidatorList::applyList
PublisherListStats applyList(std::string const &globalManifest, std::optional< std::string > const &localManifest, std::string const &blob, std::string const &signature, std::uint32_t version, std::string siteUri, std::optional< uint256 > const &hash, lock_guard const &)
Apply published list of public keys.
Definition: ValidatorList.cpp:1060
ripple::ManifestCache
Remembers manifests with the highest sequence number.
Definition: Manifest.h:231
ripple::ValidatorList::loadLists
std::vector< std::string > loadLists()
Definition: ValidatorList.cpp:1206
ripple::ValidatorList::PublisherListCollection::rawVersion
std::uint32_t rawVersion
Definition: ValidatorList.h:221
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:248
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:136
ripple::splitMessageParts
std::size_t splitMessageParts(std::vector< ValidatorList::MessageWithHash > &messages, protocol::TMValidatorListCollection const &largeMsg, std::size_t maxSize, std::size_t begin, std::size_t end)
Definition: ValidatorList.cpp:477
ripple::ProtocolFeature::ValidatorList2Propagation
@ ValidatorList2Propagation
ripple::Overlay
Manages the set of connected peers.
Definition: Overlay.h:51
ripple::ValidatorList::PublisherListCollection::current
PublisherList current
Definition: ValidatorList.h:207
ripple::PublisherStatus::unavailable
@ unavailable
ripple::ListDisposition::same_sequence
@ same_sequence
Same sequence as current list.
std::map::begin
T begin(T... args)
ripple::sha512Half
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:216
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
ripple::ListDisposition::invalid
@ invalid
Invalid format or signature.
ripple::ValidatorList::PublisherList::validFrom
TimeKeeper::time_point validFrom
Definition: ValidatorList.h:181
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:36
ripple::ValidatorList::mutex_
boost::shared_mutex mutex_
Definition: ValidatorList.h:229
ripple::TimeKeeper::now
virtual time_point now() const override=0
Returns the estimate of wall time, in network time.
ripple::ValidatorList::PublisherListStats::worstDisposition
ListDisposition worstDisposition() const
Definition: ValidatorList.cpp:92
std::count
T count(T... args)
ripple::ValidatorList::PublisherListStats::publisherKey
std::optional< PublicKey > publisherKey
Definition: ValidatorList.h:298
ripple::TrustChanges::added
hash_set< NodeID > added
Definition: ValidatorList.h:111
std::vector::empty
T empty(T... args)
ripple::TokenType::NodePublic
@ NodePublic
std::optional< std::size_t >
mutex
ripple::ValidatorList::getJson
Json::Value getJson() const
Return a JSON representation of the state of the validator list.
Definition: ValidatorList.cpp:1507
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::Peer::id
virtual id_t id() const =0
ripple::ProtocolFeature::ValidatorListPropagation
@ ValidatorListPropagation
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
std::vector::end
T end(T... args)
ripple::deserializeManifest
std::optional< Manifest > deserializeManifest(Slice s, beast::Journal journal)
Constructs Manifest from serialized string.
Definition: app/misc/impl/Manifest.cpp:53
ripple::ValidatorBlobInfo::manifest
std::optional< std::string > manifest
Definition: ValidatorList.h:124
ripple::ValidatorList::PublisherListStats::dispositions
std::map< ListDisposition, std::size_t > dispositions
Definition: ValidatorList.h:297
numeric
std::max
T max(T... args)
ripple::ValidatorList::trusted
bool trusted(PublicKey const &identity) const
Returns true if public key is trusted.
Definition: ValidatorList.cpp:1367
ripple::ValidatorList::trustedMasterKeys_
hash_set< PublicKey > trustedMasterKeys_
Definition: ValidatorList.h:243
ripple::ValidatorList::getNegativeUNL
hash_set< PublicKey > getNegativeUNL() const
get the master public keys of Negative UNL validators
Definition: ValidatorList.cpp:1940
ripple::PublisherStatus
PublisherStatus
Definition: ValidatorList.h:87
ripple::splitMessage
std::size_t splitMessage(std::vector< ValidatorList::MessageWithHash > &messages, protocol::TMValidatorListCollection const &largeMsg, std::size_t maxSize, std::size_t begin=0, std::size_t end=0)
Definition: ValidatorList.cpp:456
ripple::ValidatorList::applyLists
PublisherListStats applyLists(std::string const &manifest, std::uint32_t version, std::vector< ValidatorBlobInfo > const &blobs, std::string siteUri, std::optional< uint256 > const &hash={})
Apply multiple published lists of public keys.
Definition: ValidatorList.cpp:922
ripple::ValidatorList::PublisherListCollection::rawManifest
std::string rawManifest
Definition: ValidatorList.h:220
ripple::PublisherStatus::revoked
@ revoked
ripple::ValidatorList::count
std::size_t count() const
Return the number of configured validator list sites.
Definition: ValidatorList.cpp:1459
ripple::ManifestCache::applyManifest
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
Definition: app/misc/impl/Manifest.cpp:363
ripple::strUnHex
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Definition: StringUtilities.h:50
shared_mutex
beast::abstract_clock< NetClock >::time_point
typename NetClock ::time_point time_point
Definition: abstract_clock.h:63
ripple::ValidatorList::publisherLists_
hash_map< PublicKey, PublisherListCollection > publisherLists_
Definition: ValidatorList.h:237
ripple::ValidatorList::for_each_available
void for_each_available(std::function< void(std::string const &manifest, std::uint32_t version, std::map< std::size_t, ValidatorBlobInfo > const &blobInfos, PublicKey const &pubKey, std::size_t maxSequence, uint256 const &hash)> func) const
Invokes the callback once for every available publisher list's raw data members.
Definition: ValidatorList.cpp:1651
ripple::ValidatorList::keyListings_
hash_map< PublicKey, std::size_t > keyListings_
Definition: ValidatorList.h:240
ripple::ListDisposition::accepted
@ accepted
List is valid.
ripple::ValidatorList::quorum_
std::atomic< std::size_t > quorum_
Definition: ValidatorList.h:233
beast::abstract_clock< NetClock >::duration
typename NetClock ::duration duration
Definition: abstract_clock.h:62
ripple::PublisherStatus::available
@ available
ripple::PublisherStatus::expired
@ expired
ripple::ValidatorList::updatePublisherList
void updatePublisherList(PublicKey const &pubKey, PublisherList const &current, std::vector< PublicKey > const &oldList, lock_guard const &)
Definition: ValidatorList.cpp:996
ripple::ValidatorList::MessageWithHash::numVLs
std::size_t numVLs
Definition: ValidatorList.h:312
std::shared_lock
STL class.
ripple::ValidatorList::PublisherList
Definition: ValidatorList.h:174
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::buildValidatorListMessage
std::size_t buildValidatorListMessage(std::vector< ValidatorList::MessageWithHash > &messages, std::uint32_t rawVersion, std::string const &rawManifest, ValidatorBlobInfo const &currentBlob, std::size_t maxSize)
Definition: ValidatorList.cpp:540
ripple::ValidatorList::minimumQuorum_
std::optional< std::size_t > minimumQuorum_
Definition: ValidatorList.h:234
ripple::Peer
Represents a peer connection in the overlay.
Definition: ripple/overlay/Peer.h:45
std::next
T next(T... args)
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469