rippled
Loading...
Searching...
No Matches
ValidatorSite.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2016 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 <xrpld/app/misc/ValidatorList.h>
21#include <xrpld/app/misc/ValidatorSite.h>
22#include <xrpld/app/misc/detail/WorkFile.h>
23#include <xrpld/app/misc/detail/WorkPlain.h>
24#include <xrpld/app/misc/detail/WorkSSL.h>
25#include <xrpl/json/json_reader.h>
26#include <xrpl/protocol/digest.h>
27#include <xrpl/protocol/jss.h>
28
29#include <algorithm>
30
31namespace ripple {
32
35unsigned short constexpr max_redirects = 3;
36
38{
39 if (!parseUrl(pUrl, uri))
40 throw std::runtime_error("URI '" + uri + "' cannot be parsed");
41
42 if (pUrl.scheme == "file")
43 {
44 if (!pUrl.domain.empty())
45 throw std::runtime_error("file URI cannot contain a hostname");
46
47#if BOOST_OS_WINDOWS
48 // Paths on Windows need the leading / removed
49 if (pUrl.path[0] == '/')
51#endif
52
53 if (pUrl.path.empty())
54 throw std::runtime_error("file URI must contain a path");
55 }
56 else if (pUrl.scheme == "http")
57 {
58 if (pUrl.domain.empty())
59 throw std::runtime_error("http URI must contain a hostname");
60
61 if (!pUrl.port)
62 pUrl.port = 80;
63 }
64 else if (pUrl.scheme == "https")
65 {
66 if (pUrl.domain.empty())
67 throw std::runtime_error("https URI must contain a hostname");
68
69 if (!pUrl.port)
70 pUrl.port = 443;
71 }
72 else
73 throw std::runtime_error("Unsupported scheme: '" + pUrl.scheme + "'");
74}
75
77 : loadedResource{std::make_shared<Resource>(std::move(uri))}
79 , redirCount{0}
81 , nextRefresh{clock_type::now()}
84{
85}
86
88 Application& app,
91 : app_{app}
92 , j_{j ? *j : app_.logs().journal("ValidatorSite")}
93 , timer_{app_.getIOService()}
94 , fetching_{false}
95 , pending_{false}
96 , stopping_{false}
97 , requestTimeout_{timeout}
98{
99}
100
102{
104 if (timer_.expires_at() > clock_type::time_point{})
105 {
106 if (!stopping_)
107 {
108 lock.unlock();
109 stop();
110 }
111 else
112 {
113 cv_.wait(lock, [&] { return !fetching_; });
114 }
115 }
116}
117
118bool
120{
121 auto const sites = app_.validators().loadLists();
122 return sites.empty() || load(sites, lock_sites);
123}
124
125bool
127{
128 JLOG(j_.debug()) << "Loading configured validator list sites";
129
131
132 return load(siteURIs, lock);
133}
134
135bool
137 std::vector<std::string> const& siteURIs,
138 std::lock_guard<std::mutex> const& lock_sites)
139{
140 // If no sites are provided, act as if a site failed to load.
141 if (siteURIs.empty())
142 {
143 return missingSite(lock_sites);
144 }
145
146 for (auto const& uri : siteURIs)
147 {
148 try
149 {
150 sites_.emplace_back(uri);
151 }
152 catch (std::exception const& e)
153 {
154 JLOG(j_.error())
155 << "Invalid validator site uri: " << uri << ": " << e.what();
156 return false;
157 }
158 }
159
160 JLOG(j_.debug()) << "Loaded " << siteURIs.size() << " sites";
161
162 return true;
163}
164
165void
167{
170 if (timer_.expires_at() == clock_type::time_point{})
171 setTimer(l0, l1);
172}
173
174void
176{
178 cv_.wait(lock, [&] { return !pending_; });
179}
180
181void
183{
185 stopping_ = true;
186 // work::cancel() must be called before the
187 // cv wait in order to kick any asio async operations
188 // that might be pending.
189 if (auto sp = work_.lock())
190 sp->cancel();
191 cv_.wait(lock, [&] { return !fetching_; });
192
193 // docs indicate cancel() can throw, but this should be
194 // reconsidered if it changes to noexcept
195 try
196 {
197 timer_.cancel();
198 }
199 catch (boost::system::system_error const&)
200 {
201 }
202 stopping_ = false;
203 pending_ = false;
204 cv_.notify_all();
205}
206
207void
209 std::lock_guard<std::mutex> const& site_lock,
210 std::lock_guard<std::mutex> const& state_lock)
211{
212 auto next = std::min_element(
213 sites_.begin(), sites_.end(), [](Site const& a, Site const& b) {
214 return a.nextRefresh < b.nextRefresh;
215 });
216
217 if (next != sites_.end())
218 {
219 pending_ = next->nextRefresh <= clock_type::now();
220 cv_.notify_all();
221 timer_.expires_at(next->nextRefresh);
222 auto idx = std::distance(sites_.begin(), next);
223 timer_.async_wait([this, idx](boost::system::error_code const& ec) {
224 this->onTimer(idx, ec);
225 });
226 }
227}
228
229void
232 std::size_t siteIdx,
233 std::lock_guard<std::mutex> const& sites_lock)
234{
235 fetching_ = true;
236 sites_[siteIdx].activeResource = resource;
238 auto timeoutCancel = [this]() {
239 std::lock_guard lock_state{state_mutex_};
240 // docs indicate cancel_one() can throw, but this
241 // should be reconsidered if it changes to noexcept
242 try
243 {
244 timer_.cancel_one();
245 }
246 catch (boost::system::system_error const&)
247 {
248 }
249 };
250 auto onFetch = [this, siteIdx, timeoutCancel](
251 error_code const& err,
252 endpoint_type const& endpoint,
253 detail::response_type&& resp) {
254 timeoutCancel();
255 onSiteFetch(err, endpoint, std::move(resp), siteIdx);
256 };
257
258 auto onFetchFile = [this, siteIdx, timeoutCancel](
259 error_code const& err, std::string const& resp) {
260 timeoutCancel();
261 onTextFetch(err, resp, siteIdx);
262 };
263
264 JLOG(j_.debug()) << "Starting request for " << resource->uri;
265
266 if (resource->pUrl.scheme == "https")
267 {
268 // can throw...
269 sp = std::make_shared<detail::WorkSSL>(
270 resource->pUrl.domain,
271 resource->pUrl.path,
272 std::to_string(*resource->pUrl.port),
274 j_,
275 app_.config(),
276 sites_[siteIdx].lastRequestEndpoint,
277 sites_[siteIdx].lastRequestSuccessful,
278 onFetch);
279 }
280 else if (resource->pUrl.scheme == "http")
281 {
282 sp = std::make_shared<detail::WorkPlain>(
283 resource->pUrl.domain,
284 resource->pUrl.path,
285 std::to_string(*resource->pUrl.port),
287 sites_[siteIdx].lastRequestEndpoint,
288 sites_[siteIdx].lastRequestSuccessful,
289 onFetch);
290 }
291 else
292 {
293 BOOST_ASSERT(resource->pUrl.scheme == "file");
294 sp = std::make_shared<detail::WorkFile>(
295 resource->pUrl.path, app_.getIOService(), onFetchFile);
296 }
297
298 sites_[siteIdx].lastRequestSuccessful = false;
299 work_ = sp;
300 sp->run();
301 // start a timer for the request, which shouldn't take more
302 // than requestTimeout_ to complete
303 std::lock_guard lock_state{state_mutex_};
304 timer_.expires_after(requestTimeout_);
305 timer_.async_wait([this, siteIdx](boost::system::error_code const& ec) {
306 this->onRequestTimeout(siteIdx, ec);
307 });
308}
309
310void
312{
313 if (ec)
314 return;
315
316 {
317 std::lock_guard lock_site{sites_mutex_};
318 // In some circumstances, both this function and the response
319 // handler (onSiteFetch or onTextFetch) can get queued and
320 // processed. In all observed cases, the response handler
321 // processes a network error. Usually, this function runs first,
322 // but on extremely rare occasions, the response handler can run
323 // first, which will leave activeResource empty.
324 auto const& site = sites_[siteIdx];
325 if (site.activeResource)
326 JLOG(j_.warn()) << "Request for " << site.activeResource->uri
327 << " took too long";
328 else
329 JLOG(j_.error()) << "Request took too long, but a response has "
330 "already been processed";
331 }
332
333 std::lock_guard lock_state{state_mutex_};
334 if (auto sp = work_.lock())
335 sp->cancel();
336}
337
338void
340{
341 if (ec)
342 {
343 // Restart the timer if any errors are encountered, unless the error
344 // is from the wait operation being aborted due to a shutdown request.
345 if (ec != boost::asio::error::operation_aborted)
346 onSiteFetch(ec, {}, detail::response_type{}, siteIdx);
347 return;
348 }
349
350 try
351 {
353 sites_[siteIdx].nextRefresh =
354 clock_type::now() + sites_[siteIdx].refreshInterval;
355 sites_[siteIdx].redirCount = 0;
356 // the WorkSSL client ctor can throw if SSL init fails
357 makeRequest(sites_[siteIdx].startingResource, siteIdx, lock);
358 }
359 catch (std::exception const& ex)
360 {
361 JLOG(j_.error()) << "Exception in " << __func__ << ": " << ex.what();
363 boost::system::error_code{-1, boost::system::generic_category()},
364 {},
366 siteIdx);
367 }
368}
369
370void
372 std::string const& res,
373 std::size_t siteIdx,
374 std::lock_guard<std::mutex> const& sites_lock)
375{
376 Json::Value const body = [&res, siteIdx, this]() {
377 Json::Reader r;
378 Json::Value body;
379 if (!r.parse(res.data(), body))
380 {
381 JLOG(j_.warn()) << "Unable to parse JSON response from "
382 << sites_[siteIdx].activeResource->uri;
383 throw std::runtime_error{"bad json"};
384 }
385 return body;
386 }();
387
388 auto const [valid, version, blobs] = [&body]() {
389 // Check the easy fields first
390 bool valid = body.isObject() && body.isMember(jss::manifest) &&
391 body[jss::manifest].isString() && body.isMember(jss::version) &&
392 body[jss::version].isInt();
393 // Check the version-specific blob & signature fields
394 std::uint32_t version;
396 if (valid)
397 {
398 version = body[jss::version].asUInt();
399 blobs = ValidatorList::parseBlobs(version, body);
400 valid = !blobs.empty();
401 }
402 return std::make_tuple(valid, version, blobs);
403 }();
404
405 if (!valid)
406 {
407 JLOG(j_.warn()) << "Missing fields in JSON response from "
408 << sites_[siteIdx].activeResource->uri;
409 throw std::runtime_error{"missing fields"};
410 }
411
412 auto const manifest = body[jss::manifest].asString();
413 XRPL_ASSERT(
414 version == body[jss::version].asUInt(),
415 "ripple::ValidatorSite::parseJsonResponse : version match");
416 auto const& uri = sites_[siteIdx].activeResource->uri;
417 auto const hash = sha512Half(manifest, blobs, version);
418 auto const applyResult = app_.validators().applyListsAndBroadcast(
419 manifest,
420 version,
421 blobs,
422 uri,
423 hash,
424 app_.overlay(),
426 app_.getOPs());
427
428 sites_[siteIdx].lastRefreshStatus.emplace(
429 Site::Status{clock_type::now(), applyResult.bestDisposition(), ""});
430
431 for (auto const& [disp, count] : applyResult.dispositions)
432 {
433 switch (disp)
434 {
436 JLOG(j_.debug()) << "Applied " << count
437 << " new validator list(s) from " << uri;
438 break;
440 JLOG(j_.debug()) << "Applied " << count
441 << " expired validator list(s) from " << uri;
442 break;
444 JLOG(j_.debug())
445 << "Ignored " << count
446 << " validator list(s) with current sequence from " << uri;
447 break;
449 JLOG(j_.debug()) << "Processed " << count
450 << " future validator list(s) from " << uri;
451 break;
453 JLOG(j_.debug())
454 << "Ignored " << count
455 << " validator list(s) with future known sequence from "
456 << uri;
457 break;
459 JLOG(j_.warn()) << "Ignored " << count
460 << "stale validator list(s) from " << uri;
461 break;
463 JLOG(j_.warn()) << "Ignored " << count
464 << " untrusted validator list(s) from " << uri;
465 break;
467 JLOG(j_.warn()) << "Ignored " << count
468 << " invalid validator list(s) from " << uri;
469 break;
471 JLOG(j_.warn())
472 << "Ignored " << count
473 << " unsupported version validator list(s) from " << uri;
474 break;
475 default:
476 BOOST_ASSERT(false);
477 }
478 }
479
480 if (body.isMember(jss::refresh_interval) &&
481 body[jss::refresh_interval].isNumeric())
482 {
483 using namespace std::chrono_literals;
484 std::chrono::minutes const refresh = std::clamp(
485 std::chrono::minutes{body[jss::refresh_interval].asUInt()},
486 1min,
488 sites_[siteIdx].refreshInterval = refresh;
489 sites_[siteIdx].nextRefresh =
490 clock_type::now() + sites_[siteIdx].refreshInterval;
491 }
492}
493
497 std::size_t siteIdx,
498 std::lock_guard<std::mutex> const& sites_lock)
499{
500 using namespace boost::beast::http;
502 if (res.find(field::location) == res.end() || res[field::location].empty())
503 {
504 JLOG(j_.warn()) << "Request for validator list at "
505 << sites_[siteIdx].activeResource->uri
506 << " returned a redirect with no Location.";
507 throw std::runtime_error{"missing location"};
508 }
509
510 if (sites_[siteIdx].redirCount == max_redirects)
511 {
512 JLOG(j_.warn()) << "Exceeded max redirects for validator list at "
513 << sites_[siteIdx].loadedResource->uri;
514 throw std::runtime_error{"max redirects"};
515 }
516
517 JLOG(j_.debug()) << "Got redirect for validator list from "
518 << sites_[siteIdx].activeResource->uri
519 << " to new location " << res[field::location];
520
521 try
522 {
523 newLocation =
524 std::make_shared<Site::Resource>(std::string(res[field::location]));
525 ++sites_[siteIdx].redirCount;
526 if (newLocation->pUrl.scheme != "http" &&
527 newLocation->pUrl.scheme != "https")
528 throw std::runtime_error(
529 "invalid scheme in redirect " + newLocation->pUrl.scheme);
530 }
531 catch (std::exception const& ex)
532 {
533 JLOG(j_.error()) << "Invalid redirect location: "
534 << res[field::location];
535 throw;
536 }
537 return newLocation;
538}
539
540void
542 boost::system::error_code const& ec,
543 endpoint_type const& endpoint,
545 std::size_t siteIdx)
546{
547 std::lock_guard lock_sites{sites_mutex_};
548 {
549 if (endpoint != endpoint_type{})
550 sites_[siteIdx].lastRequestEndpoint = endpoint;
551 JLOG(j_.debug()) << "Got completion for "
552 << sites_[siteIdx].activeResource->uri << " "
553 << endpoint;
554 auto onError = [&](std::string const& errMsg, bool retry) {
555 sites_[siteIdx].lastRefreshStatus.emplace(Site::Status{
557 if (retry)
558 sites_[siteIdx].nextRefresh =
560
561 // See if there's a copy saved locally from last time we
562 // saw the list.
563 missingSite(lock_sites);
564 };
565 if (ec)
566 {
567 JLOG(j_.warn())
568 << "Problem retrieving from "
569 << sites_[siteIdx].activeResource->uri << " " << endpoint << " "
570 << ec.value() << ":" << ec.message();
571 onError("fetch error", true);
572 }
573 else
574 {
575 try
576 {
577 using namespace boost::beast::http;
578 switch (res.result())
579 {
580 case status::ok:
581 sites_[siteIdx].lastRequestSuccessful = true;
582 parseJsonResponse(res.body(), siteIdx, lock_sites);
583 break;
584 case status::moved_permanently:
585 case status::permanent_redirect:
586 case status::found:
587 case status::temporary_redirect: {
588 auto newLocation =
589 processRedirect(res, siteIdx, lock_sites);
590 XRPL_ASSERT(
591 newLocation,
592 "ripple::ValidatorSite::onSiteFetch : non-null "
593 "validator");
594 // for perm redirects, also update our starting URI
595 if (res.result() == status::moved_permanently ||
596 res.result() == status::permanent_redirect)
597 {
598 sites_[siteIdx].startingResource = newLocation;
599 }
600 makeRequest(newLocation, siteIdx, lock_sites);
601 return; // we are still fetching, so skip
602 // state update/notify below
603 }
604 default: {
605 JLOG(j_.warn())
606 << "Request for validator list at "
607 << sites_[siteIdx].activeResource->uri << " "
608 << endpoint
609 << " returned bad status: " << res.result_int();
610 onError("bad result code", true);
611 }
612 }
613 }
614 catch (std::exception const& ex)
615 {
616 JLOG(j_.error())
617 << "Exception in " << __func__ << ": " << ex.what();
618 onError(ex.what(), false);
619 }
620 }
621 sites_[siteIdx].activeResource.reset();
622 }
623
624 std::lock_guard lock_state{state_mutex_};
625 fetching_ = false;
626 if (!stopping_)
627 setTimer(lock_sites, lock_state);
628 cv_.notify_all();
629}
630
631void
633 boost::system::error_code const& ec,
634 std::string const& res,
635 std::size_t siteIdx)
636{
637 std::lock_guard lock_sites{sites_mutex_};
638 {
639 try
640 {
641 if (ec)
642 {
643 JLOG(j_.warn()) << "Problem retrieving from "
644 << sites_[siteIdx].activeResource->uri << " "
645 << ec.value() << ": " << ec.message();
646 throw std::runtime_error{"fetch error"};
647 }
648
649 sites_[siteIdx].lastRequestSuccessful = true;
650
651 parseJsonResponse(res, siteIdx, lock_sites);
652 }
653 catch (std::exception const& ex)
654 {
655 JLOG(j_.error())
656 << "Exception in " << __func__ << ": " << ex.what();
657 sites_[siteIdx].lastRefreshStatus.emplace(Site::Status{
659 }
660 sites_[siteIdx].activeResource.reset();
661 }
662
663 std::lock_guard lock_state{state_mutex_};
664 fetching_ = false;
665 if (!stopping_)
666 setTimer(lock_sites, lock_state);
667 cv_.notify_all();
668}
669
672{
673 using namespace std::chrono;
674 using Int = Json::Value::Int;
675
677 Json::Value& jSites = (jrr[jss::validator_sites] = Json::arrayValue);
678 {
680 for (Site const& site : sites_)
681 {
684 uri << site.loadedResource->uri;
685 if (site.loadedResource != site.startingResource)
686 uri << " (redirects to " << site.startingResource->uri + ")";
687 v[jss::uri] = uri.str();
688 v[jss::next_refresh_time] = to_string(site.nextRefresh);
689 if (site.lastRefreshStatus)
690 {
691 v[jss::last_refresh_time] =
692 to_string(site.lastRefreshStatus->refreshed);
693 v[jss::last_refresh_status] =
694 to_string(site.lastRefreshStatus->disposition);
695 if (!site.lastRefreshStatus->message.empty())
696 v[jss::last_refresh_message] =
697 site.lastRefreshStatus->message;
698 }
699 v[jss::refresh_interval_min] =
700 static_cast<Int>(site.refreshInterval.count());
701 }
702 }
703 return jrr;
704}
705} // namespace ripple
T clamp(T... args)
Unserialize a JSON document into a Value.
Definition: json_reader.h:39
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:78
Represents a JSON value.
Definition: json_value.h:148
bool isString() const
UInt asUInt() const
Definition: json_value.cpp:551
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:897
bool isObject() const
Json::Int Int
Definition: json_value.h:156
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:475
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
bool isNumeric() const
bool isInt() const
Definition: json_value.cpp:998
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
Stream warn() const
Definition: Journal.h:340
virtual Config & config()=0
virtual Overlay & overlay()=0
virtual NetworkOPs & getOPs()=0
virtual ValidatorList & validators()=0
virtual boost::asio::io_service & getIOService()=0
virtual HashRouter & getHashRouter()=0
std::vector< std::string > loadLists()
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...
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...
void start()
Start fetching lists from sites.
std::condition_variable cv_
beast::Journal const j_
std::vector< Site > sites_
boost::asio::ip::tcp::endpoint endpoint_type
Definition: ValidatorSite.h:74
void stop()
Stop fetching lists from sites.
Json::Value getJson() const
Return JSON representation of configured validator sites.
bool load(std::vector< std::string > const &siteURIs)
Load configured site URIs.
void onTextFetch(boost::system::error_code const &ec, std::string const &res, std::size_t siteIdx)
Store latest list fetched from anywhere.
std::weak_ptr< detail::Work > work_
void setTimer(std::lock_guard< std::mutex > const &, std::lock_guard< std::mutex > const &)
Queue next site to be fetched lock over site_mutex_ and state_mutex_ required.
ValidatorSite(Application &app, std::optional< beast::Journal > j=std::nullopt, std::chrono::seconds timeout=std::chrono::seconds{20})
std::atomic< bool > stopping_
void join()
Wait for current fetches from sites to complete.
bool missingSite(std::lock_guard< std::mutex > const &)
If no sites are provided, or a site fails to load, get a list of local cache files from the Validator...
std::shared_ptr< Site::Resource > processRedirect(detail::response_type &res, std::size_t siteIdx, std::lock_guard< std::mutex > const &)
Interpret a redirect response.
void parseJsonResponse(std::string const &res, std::size_t siteIdx, std::lock_guard< std::mutex > const &)
Parse json response from validator list site.
void makeRequest(std::shared_ptr< Site::Resource > resource, std::size_t siteIdx, std::lock_guard< std::mutex > const &)
Initiate request to given resource.
void onRequestTimeout(std::size_t siteIdx, error_code const &ec)
request took too long
std::atomic< bool > pending_
boost::system::error_code error_code
Definition: ValidatorSite.h:72
boost::asio::basic_waitable_timer< clock_type > timer_
void onTimer(std::size_t siteIdx, error_code const &ec)
Fetch site whose time has come.
void onSiteFetch(boost::system::error_code const &ec, endpoint_type const &endpoint, detail::response_type &&res, std::size_t siteIdx)
Store latest list fetched from site.
const std::chrono::seconds requestTimeout_
std::atomic< bool > fetching_
T data(T... args)
T distance(T... args)
T empty(T... args)
T make_tuple(T... args)
T min_element(T... args)
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
TER valid(PreclaimContext const &ctx, AccountID const &src)
boost::beast::http::response< boost::beast::http::string_body > response_type
Definition: Work.h:31
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ unsupported_version
List version is not supported.
@ stale
Trusted publisher key, but seq is too old.
@ accepted
List is valid.
@ untrusted
List signed by untrusted publisher key.
@ same_sequence
Same sequence as current list.
@ pending
List will be valid in the future.
@ known_sequence
Future sequence already seen.
@ expired
List is expired, but has the largest non-pending sequence seen so far.
@ invalid
Invalid format or signature.
bool parseUrl(parsedURL &pUrl, std::string const &strUrl)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
@ manifest
Manifest.
unsigned short constexpr max_redirects
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:225
auto constexpr error_retry_interval
auto constexpr default_refresh_interval
STL namespace.
T size(T... args)
T str(T... args)
std::shared_ptr< Resource > loadedResource
the original uri as loaded from config
Definition: ValidatorSite.h:95
std::shared_ptr< Resource > startingResource
the resource to request at <timer> intervals.
endpoint_type lastRequestEndpoint
std::chrono::minutes refreshInterval
clock_type::time_point nextRefresh
std::optional< std::uint16_t > port
T substr(T... args)
T to_string(T... args)
T what(T... args)