Clio develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
ServerInfo.hpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of clio: https://github.com/XRPLF/clio
4 Copyright (c) 2023, the clio developers.
5
6 Permission to use, copy, modify, and 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#pragma once
21
22#include "data/BackendInterface.hpp"
23#include "data/DBHelpers.hpp"
24#include "etlng/ETLServiceInterface.hpp"
26#include "feed/SubscriptionManagerInterface.hpp"
27#include "rpc/Errors.hpp"
28#include "rpc/JS.hpp"
29#include "rpc/common/Specs.hpp"
30#include "rpc/common/Types.hpp"
31#include "util/Assert.hpp"
32#include "util/build/Build.hpp"
33
34#include <boost/json/conversion.hpp>
35#include <boost/json/object.hpp>
36#include <boost/json/value.hpp>
37#include <fmt/core.h>
38#include <xrpl/basics/chrono.h>
39#include <xrpl/basics/strHex.h>
40#include <xrpl/protocol/BuildInfo.h>
41#include <xrpl/protocol/ErrorCodes.h>
42#include <xrpl/protocol/Fees.h>
43#include <xrpl/protocol/Protocol.h>
44#include <xrpl/protocol/jss.h>
45
46#include <chrono>
47#include <cstddef>
48#include <cstdint>
49#include <functional>
50#include <memory>
51#include <optional>
52#include <string>
53
54namespace rpc {
55class Counters;
56} // namespace rpc
57
58namespace rpc {
59
65template <typename CountersType>
67 static constexpr auto kBACKEND_COUNTERS_KEY = "backend_counters";
68
69 std::shared_ptr<BackendInterface> backend_;
70 std::shared_ptr<feed::SubscriptionManagerInterface> subscriptions_;
71 std::shared_ptr<etlng::LoadBalancerInterface> balancer_;
72 std::shared_ptr<etlng::ETLServiceInterface const> etl_;
73 std::reference_wrapper<CountersType const> counters_;
74
75public:
79 struct Input {
80 bool backendCounters = false;
81 };
82
86 struct AdminSection {
87 boost::json::object counters;
88 std::optional<boost::json::object> backendCounters;
89 boost::json::object subscriptions;
90 boost::json::object etl;
91 };
92
97 uint32_t age = 0;
98 std::string hash;
99 ripple::LedgerIndex seq = {};
100 std::optional<ripple::Fees> fees = std::nullopt;
101 };
102
107 std::size_t size = 0;
108 bool isEnabled = false;
109 bool isFull = false;
110 ripple::LedgerIndex latestLedgerSeq = {};
111 float objectHitRate = 1.0;
112 float successorHitRate = 1.0;
113 };
114
118 struct InfoSection {
119 std::optional<AdminSection> adminSection = std::nullopt;
120 std::string completeLedgers;
121 uint32_t loadFactor = 1u;
122 std::chrono::time_point<std::chrono::system_clock> time = std::chrono::system_clock::now();
123 std::chrono::seconds uptime = {};
124 std::string clioVersion = util::build::getClioVersionString();
125 std::string xrplVersion = ripple::BuildInfo::getVersionString();
126 std::optional<boost::json::object> rippledInfo = std::nullopt;
127 ValidatedLedgerSection validatedLedger = {};
128 CacheSection cache = {};
129 bool isAmendmentBlocked = false;
130 bool isCorruptionDetected = false;
131 };
132
136 struct Output {
137 InfoSection info = {};
138
139 // validated should be sent via framework
140 bool validated = true;
141 };
142
143 using Result = HandlerReturnType<Output>;
144
155 std::shared_ptr<BackendInterface> const& backend,
156 std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptions,
157 std::shared_ptr<etlng::LoadBalancerInterface> const& balancer,
158 std::shared_ptr<etlng::ETLServiceInterface const> const& etl,
159 CountersType const& counters
160 )
161 : backend_(backend)
162 , subscriptions_(subscriptions)
163 , balancer_(balancer)
164 , etl_(etl)
165 , counters_(std::cref(counters))
166 {
167 }
168
175 static RpcSpecConstRef
176 spec([[maybe_unused]] uint32_t apiVersion)
177 {
178 static RpcSpec const kRPC_SPEC = {};
179 return kRPC_SPEC;
180 }
181
189 Result
190 process(Input input, Context const& ctx) const
191 {
192 using namespace rpc;
193 using namespace std::chrono;
194
195 auto const range = backend_->fetchLedgerRange();
196 ASSERT(range.has_value(), "ServerInfo's ledger range must be available");
197
198 auto const lgrInfo = backend_->fetchLedgerBySequence(range->maxSequence, ctx.yield);
199 if (not lgrInfo.has_value())
200 return Error{Status{RippledError::rpcINTERNAL}};
201
202 auto const fees = backend_->fetchFees(lgrInfo->seq, ctx.yield);
203 if (not fees.has_value())
204 return Error{Status{RippledError::rpcINTERNAL}};
205
206 auto output = Output{};
207 auto const sinceEpoch = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
208 auto const age = static_cast<int32_t>(sinceEpoch) -
209 static_cast<int32_t>(lgrInfo->closeTime.time_since_epoch().count()) -
210 static_cast<int32_t>(kRIPPLE_EPOCH_START);
211
212 output.info.completeLedgers = fmt::format("{}-{}", range->minSequence, range->maxSequence);
213
214 if (ctx.isAdmin) {
215 output.info.adminSection = {
216 .counters = counters_.get().report(),
217 .backendCounters = input.backendCounters ? std::make_optional(backend_->stats()) : std::nullopt,
218 .subscriptions = subscriptions_->report(),
219 .etl = etl_->getInfo()
220 };
221 }
222
223 auto const serverInfoRippled =
224 balancer_->forwardToRippled({{"command", "server_info"}}, ctx.clientIp, ctx.isAdmin, ctx.yield);
225
226 if (serverInfoRippled && !serverInfoRippled->contains(JS(error))) {
227 if (serverInfoRippled->contains(JS(result)) &&
228 serverInfoRippled->at(JS(result)).as_object().contains(JS(info))) {
229 output.info.rippledInfo = serverInfoRippled->at(JS(result)).as_object().at(JS(info)).as_object();
230 }
231 }
232
233 output.info.validatedLedger.age = age < 0 ? 0 : age;
234 output.info.validatedLedger.hash = ripple::strHex(lgrInfo->hash);
235 output.info.validatedLedger.seq = lgrInfo->seq;
236 output.info.validatedLedger.fees = fees;
237 output.info.cache.size = backend_->cache().size();
238 output.info.cache.isFull = backend_->cache().isFull();
239 output.info.cache.latestLedgerSeq = backend_->cache().latestLedgerSequence();
240 output.info.cache.objectHitRate = backend_->cache().getObjectHitRate();
241 output.info.cache.successorHitRate = backend_->cache().getSuccessorHitRate();
242 output.info.cache.isEnabled = not backend_->cache().isDisabled();
243 output.info.uptime = counters_.get().uptime();
244 output.info.isAmendmentBlocked = etl_->isAmendmentBlocked();
245 output.info.isCorruptionDetected = etl_->isCorruptionDetected();
246
247 return output;
248 }
249
250private:
251 friend void
252 tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output)
253 {
254 using boost::json::value_from;
255
256 jv = {
257 {JS(info), value_from(output.info)},
258 {JS(validated), output.validated},
259 };
260 }
261
262 friend void
263 tag_invoke(boost::json::value_from_tag, boost::json::value& jv, InfoSection const& info)
264 {
265 using boost::json::value_from;
266 using ripple::to_string;
267
268 jv = {
269 {JS(complete_ledgers), info.completeLedgers},
270 {JS(load_factor), info.loadFactor},
271 {JS(time), to_string(std::chrono::floor<std::chrono::microseconds>(info.time))},
272 {JS(uptime), info.uptime.count()},
273 {"clio_version", info.clioVersion},
274 {"libxrpl_version", info.xrplVersion},
275 {JS(validated_ledger), value_from(info.validatedLedger)},
276 {"cache", value_from(info.cache)},
277 };
278
279 if (info.isAmendmentBlocked)
280 jv.as_object()[JS(amendment_blocked)] = true;
281
282 if (info.isCorruptionDetected)
283 jv.as_object()["corruption_detected"] = true;
284
285 if (info.rippledInfo) {
286 auto const& rippledInfo = info.rippledInfo.value();
287
288 if (rippledInfo.contains(JS(load_factor)))
289 jv.as_object()[JS(load_factor)] = rippledInfo.at(JS(load_factor));
290 if (rippledInfo.contains(JS(validation_quorum)))
291 jv.as_object()[JS(validation_quorum)] = rippledInfo.at(JS(validation_quorum));
292 if (rippledInfo.contains(JS(build_version)))
293 jv.as_object()["rippled_version"] = rippledInfo.at(JS(build_version));
294 if (rippledInfo.contains(JS(network_id)))
295 jv.as_object()[JS(network_id)] = rippledInfo.at(JS(network_id));
296 }
297
298 if (info.adminSection) {
299 jv.as_object()["etl"] = info.adminSection->etl;
300 jv.as_object()[JS(counters)] = info.adminSection->counters;
301 jv.as_object()[JS(counters)].as_object()["subscriptions"] = info.adminSection->subscriptions;
302 if (info.adminSection->backendCounters.has_value()) {
303 jv.as_object()[kBACKEND_COUNTERS_KEY] = *info.adminSection->backendCounters;
304 }
305 }
306 }
307
308 friend void
309 tag_invoke(boost::json::value_from_tag, boost::json::value& jv, ValidatedLedgerSection const& validated)
310 {
311 jv = {
312 {JS(age), validated.age},
313 {JS(hash), validated.hash},
314 {JS(seq), validated.seq},
315 {JS(base_fee_xrp), validated.fees->base.decimalXRP()},
316 {JS(reserve_base_xrp), validated.fees->reserve.decimalXRP()},
317 {JS(reserve_inc_xrp), validated.fees->increment.decimalXRP()},
318 };
319 }
320
321 friend void
322 tag_invoke(boost::json::value_from_tag, boost::json::value& jv, CacheSection const& cache)
323 {
324 jv = {
325 {"size", cache.size},
326 {"is_enabled", cache.isEnabled},
327 {"is_full", cache.isFull},
328 {"latest_ledger_seq", cache.latestLedgerSeq},
329 {"object_hit_rate", cache.objectHitRate},
330 {"successor_hit_rate", cache.successorHitRate},
331 };
332 }
333
334 friend Input
335 tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv)
336 {
337 auto input = BaseServerInfoHandler::Input{};
338 auto const jsonObject = jv.as_object();
339 if (jsonObject.contains(kBACKEND_COUNTERS_KEY) && jsonObject.at(kBACKEND_COUNTERS_KEY).is_bool())
340 input.backendCounters = jv.at(kBACKEND_COUNTERS_KEY).as_bool();
341 return input;
342 }
343};
344
352
353} // namespace rpc
static constexpr std::uint32_t kRIPPLE_EPOCH_START
The ripple epoch start timestamp. Midnight on 1st January 2000.
Definition DBHelpers.hpp:318
Contains common functionality for handling the server_info command.
Definition ServerInfo.hpp:66
Result process(Input input, Context const &ctx) const
Process the ServerInfo command.
Definition ServerInfo.hpp:190
static RpcSpecConstRef spec(uint32_t apiVersion)
Returns the API specification for the command.
Definition ServerInfo.hpp:176
BaseServerInfoHandler(std::shared_ptr< BackendInterface > const &backend, std::shared_ptr< feed::SubscriptionManagerInterface > const &subscriptions, std::shared_ptr< etlng::LoadBalancerInterface > const &balancer, std::shared_ptr< etlng::ETLServiceInterface const > const &etl, CountersType const &counters)
Construct a new BaseServerInfoHandler object.
Definition ServerInfo.hpp:154
This namespace contains everything to do with the ETL and ETL sources.
Definition CacheLoader.hpp:37
This namespace contains all the RPC logic and handlers.
Definition AMMHelpers.cpp:37
RpcSpec const & RpcSpecConstRef
An alias for a const reference to RpcSpec.
Definition Specs.hpp:145
std::expected< OutputType, Status > HandlerReturnType
Return type for each individual handler.
Definition Types.hpp:81
std::unexpected< Status > Error
The type that represents just the error part of MaybeError.
Definition Types.hpp:75
A struct to hold the admin section of the output.
Definition ServerInfo.hpp:86
A struct to hold the cache section of the output.
Definition ServerInfo.hpp:106
A struct to hold the info section of the output.
Definition ServerInfo.hpp:118
A struct to hold the input data for the command.
Definition ServerInfo.hpp:79
A struct to hold the output data of the command.
Definition ServerInfo.hpp:136
A struct to hold the validated ledger section of the output.
Definition ServerInfo.hpp:96
Context of an RPC call.
Definition Types.hpp:118
Result type used to return responses or error statuses to the Webserver subsystem.
Definition Types.hpp:129
Represents a Specification of an entire RPC command.
Definition Specs.hpp:98
A status returned from any RPC handler.
Definition Errors.hpp:82