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