xrpld
Loading...
Searching...
No Matches
xrpld/rpc/handlers/ledger/Ledger.cpp
1#include <xrpld/rpc/handlers/ledger/Ledger.h>
2
3#include <xrpld/app/ledger/LedgerToJson.h>
4#include <xrpld/app/main/Application.h>
5#include <xrpld/rpc/Context.h>
6#include <xrpld/rpc/GRPCHandlers.h>
7#include <xrpld/rpc/Role.h>
8#include <xrpld/rpc/Status.h>
9#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
10
11#include <xrpl/basics/Log.h>
12#include <xrpl/beast/utility/instrumentation.h>
13#include <xrpl/json/json_value.h>
14#include <xrpl/protocol/ErrorCodes.h>
15#include <xrpl/protocol/Indexes.h>
16#include <xrpl/protocol/LedgerFormats.h>
17#include <xrpl/protocol/LedgerHeader.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/Serializer.h>
20#include <xrpl/protocol/jss.h>
21#include <xrpl/resource/Fees.h>
22#include <xrpl/server/LoadFeeTrack.h>
23#include <xrpl/shamap/SHAMap.h>
24
25#include <grpcpp/support/status.h>
26#include <org/xrpl/rpc/v1/get_ledger.pb.h>
27
28#include <chrono>
29#include <exception>
30#include <expected>
31#include <limits>
32#include <memory>
33#include <string>
34#include <utility>
35
36namespace xrpl {
37namespace RPC {
38
42
45{
46 auto const& params = context_.params;
47
48 auto getBool = [&](json::StaticString const& field) -> std::expected<bool, Status> {
49 if (!params.isMember(field))
50 {
51 return false;
52 }
53 if (!params[field].isBool())
54 {
56 }
57
58 return params[field].asBool();
59 };
60
61 auto const full = getBool(jss::full);
62 auto const transactions = getBool(jss::transactions);
63 auto const accounts = getBool(jss::accounts);
64 auto const expand = getBool(jss::expand);
65 auto const binary = getBool(jss::binary);
66 auto const ownerFunds = getBool(jss::owner_funds);
67 auto const queue = getBool(jss::queue);
68
69 if (!full.has_value())
70 return full.error();
71 if (!transactions.has_value())
72 return transactions.error();
73 if (!accounts.has_value())
74 return accounts.error();
75 if (!expand.has_value())
76 return expand.error();
77 if (!binary.has_value())
78 return binary.error();
79 if (!ownerFunds.has_value())
80 return ownerFunds.error();
81 if (!queue.has_value())
82 return queue.error();
83
84 options_ = (*full ? static_cast<int>(LedgerFill::Options::Full) : 0) |
85 (*expand ? static_cast<int>(LedgerFill::Options::Expand) : 0) |
86 (*transactions ? static_cast<int>(LedgerFill::Options::DumpTxrp) : 0) |
87 (*accounts ? static_cast<int>(LedgerFill::Options::DumpState) : 0) |
88 (*binary ? static_cast<int>(LedgerFill::Options::Binary) : 0) |
89 (*ownerFunds ? static_cast<int>(LedgerFill::Options::OwnerFunds) : 0) |
90 (*queue ? static_cast<int>(LedgerFill::Options::DumpQueue) : 0);
91
92 bool const needsLedger = params.isMember(jss::ledger) || params.isMember(jss::ledger_hash) ||
93 params.isMember(jss::ledger_index);
94 if (!needsLedger)
95 return Status::kOK;
96 if (auto s = lookupLedger(ledger_, context_, result_))
97 return s;
98
99 if (*full || *accounts)
100 {
101 // Until some sane way to get full ledgers has been implemented,
102 // disallow retrieving all state nodes.
103 if (!isUnlimited(context_.role))
104 return RpcNoPermission;
105
106 if (context_.app.getFeeTrack().isLoadedLocal() && !isUnlimited(context_.role))
107 {
108 return RpcTooBusy;
109 }
111 }
112
113 if (*queue)
114 {
115 if (!ledger_ || !ledger_->open())
116 {
117 // It doesn't make sense to request the queue
118 // with a non-existent or closed/validated ledger.
119 return RpcInvalidParams;
120 }
121
122 queueTxs_ = context_.app.getTxQ().getTxs();
123 }
124
125 return Status::kOK;
126}
127
128void
130{
131 if (ledger_)
132 {
133 copyFrom(value, result_);
135 }
136 else
137 {
138 auto& master = context_.app.getLedgerMaster();
139 {
140 auto& closed = value[jss::closed] = json::ValueType::Object;
141 addJson(closed, {*master.getClosedLedger(), &context_, 0});
142 }
143 {
144 auto& open = value[jss::open] = json::ValueType::Object;
145 addJson(open, {*master.getCurrentLedger(), &context_, 0});
146 }
147 }
148
150 if (context_.params.isMember(jss::type))
151 {
153 w[jss::id] = WarnRpcFieldsDeprecated;
154 w[jss::message] =
155 "Some fields from your request are deprecated. Please check the "
156 "documentation at "
157 "https://xrpl.org/docs/references/http-websocket-apis/ "
158 "and update your request. Field `type` is deprecated.";
159 }
160
161 if (warnings.size() != 0u)
162 value[jss::warnings] = std::move(warnings);
163}
164
165} // namespace RPC
166
169{
170 auto begin = std::chrono::system_clock::now();
171 org::xrpl::rpc::v1::GetLedgerRequest const& request = context.params;
172 org::xrpl::rpc::v1::GetLedgerResponse response;
173 grpc::Status const status = grpc::Status::OK;
174
176 if (auto status = RPC::ledgerFromRequest(ledger, context))
177 {
178 grpc::Status errorStatus;
179 if (status.toErrorCode() == RpcInvalidParams)
180 {
181 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
182 }
183 else
184 {
185 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
186 }
187 return {response, errorStatus};
188 }
189
190 Serializer s;
191 addRaw(ledger->header(), s, true);
192
193 response.set_ledger_header(s.peekData().data(), s.getLength());
194
195 if (request.transactions())
196 {
197 try
198 {
199 for (auto& i : ledger->txs)
200 {
201 XRPL_ASSERT(i.first, "xrpl::doLedgerGrpc : non-null transaction");
202 if (request.expand())
203 {
204 auto txn = response.mutable_transactions_list()->add_transactions();
205 Serializer const sTxn = i.first->getSerializer();
206 txn->set_transaction_blob(sTxn.data(), sTxn.getLength());
207 if (i.second)
208 {
209 Serializer const sMeta = i.second->getSerializer();
210 txn->set_metadata_blob(sMeta.data(), sMeta.getLength());
211 }
212 }
213 else
214 {
215 auto const& hash = i.first->getTransactionID();
216 response.mutable_hashes_list()->add_hashes(hash.data(), hash.size());
217 }
218 }
219 }
220 catch (std::exception const& e)
221 {
222 JLOG(context.j.error()) << __func__ << " - Error deserializing transaction in ledger "
223 << ledger->header().seq
224 << " . skipping transaction and following transactions. You "
225 "should look into this further";
226 }
227 }
228
229 if (request.get_objects())
230 {
232 context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1);
233
235 if (!base)
236 {
237 grpc::Status const errorStatus{
238 grpc::StatusCode::NOT_FOUND, "parent ledger not validated"};
239 return {response, errorStatus};
240 }
241
242 std::shared_ptr<Ledger const> const desired =
244 if (!desired)
245 {
246 grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "ledger not validated"};
247 return {response, errorStatus};
248 }
249 SHAMap::Delta differences;
250
251 int const maxDifferences = std::numeric_limits<int>::max();
252
253 bool const res = base->stateMap().compare(desired->stateMap(), differences, maxDifferences);
254 if (!res)
255 {
256 grpc::Status const errorStatus{
257 grpc::StatusCode::RESOURCE_EXHAUSTED,
258 "too many differences between specified ledgers"};
259 return {response, errorStatus};
260 }
261
262 for (auto& [k, v] : differences)
263 {
264 auto obj = response.mutable_ledger_objects()->add_objects();
265 auto inBase = v.first;
266 auto inDesired = v.second;
267
268 obj->set_key(k.data(), k.size());
269 if (inDesired)
270 {
271 XRPL_ASSERT(inDesired->size() > 0, "xrpl::doLedgerGrpc : non-empty desired");
272 obj->set_data(inDesired->data(), inDesired->size());
273 }
274 if (inBase && inDesired)
275 {
276 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::MODIFIED);
277 }
278 else if (inBase && !inDesired)
279 {
280 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
281 }
282 else
283 {
284 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
285 }
286 auto const blob = inDesired ? inDesired->slice() : inBase->slice();
287 auto const objectType = static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
288
289 if (request.get_object_neighbors())
290 {
291 if (!(inBase && inDesired))
292 {
293 auto lb = desired->stateMap().lowerBound(k);
294 auto ub = desired->stateMap().upperBound(k);
295 if (lb != desired->stateMap().end())
296 obj->set_predecessor(lb->key().data(), lb->key().size());
297 if (ub != desired->stateMap().end())
298 obj->set_successor(ub->key().data(), ub->key().size());
299 if (objectType == ltDIR_NODE)
300 {
301 auto sle = std::make_shared<SLE>(SerialIter{blob}, k);
302 if (!sle->isFieldPresent(sfOwner))
303 {
304 auto bookBase = keylet::quality({ltDIR_NODE, k}, 0);
305 if (!inBase && inDesired)
306 {
307 auto firstBook = desired->stateMap().upperBound(bookBase.key);
308 if (firstBook != desired->stateMap().end() &&
309 firstBook->key() < getQualityNext(bookBase.key) &&
310 firstBook->key() == k)
311 {
312 auto succ = response.add_book_successors();
313 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
314 succ->set_first_book(
315 firstBook->key().data(), firstBook->key().size());
316 }
317 }
318 if (inBase && !inDesired)
319 {
320 auto oldFirstBook = base->stateMap().upperBound(bookBase.key);
321 if (oldFirstBook != base->stateMap().end() &&
322 oldFirstBook->key() < getQualityNext(bookBase.key) &&
323 oldFirstBook->key() == k)
324 {
325 auto succ = response.add_book_successors();
326 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
327 auto newFirstBook =
328 desired->stateMap().upperBound(bookBase.key);
329
330 if (newFirstBook != desired->stateMap().end() &&
331 newFirstBook->key() < getQualityNext(bookBase.key))
332 {
333 succ->set_first_book(
334 newFirstBook->key().data(), newFirstBook->key().size());
335 }
336 }
337 }
338 }
339 }
340 }
341 }
342 }
343 response.set_objects_included(true);
344 response.set_object_neighbors_included(request.get_object_neighbors());
345 response.set_skiplist_included(true);
346 }
347
348 response.set_validated(context.ledgerMaster.isValidated(*ledger));
349
351 auto duration =
353 // Guard the per-item rates: an empty ledger has zero objects and/or zero
354 // transactions, and dividing by zero is undefined for these doubles.
355 auto const numObjects = response.ledger_objects().objects_size();
356 auto const numTxns = response.transactions_list().transactions_size();
357 std::string const msPerObj = numObjects > 0 ? std::to_string(duration / numObjects) : "n/a";
358 std::string const msPerTxn = numTxns > 0 ? std::to_string(duration / numTxns) : "n/a";
359 JLOG(context.j.warn()) << __func__ << " - Extract time = " << duration
360 << " - num objects = " << numObjects << " - num txns = " << numTxns
361 << " - ms per obj " << msPerObj << " - ms per txn " << msPerTxn;
362
363 return {response, status};
364}
365} // namespace xrpl
Stream error() const
Definition Journal.h:315
Stream warn() const
Definition Journal.h:309
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
bool isValidated(ReadView const &ledger)
std::shared_ptr< ReadView const > ledger_
std::map< uint256, DeltaItem > Delta
Definition SHAMap.h:104
Blob const & peekData() const
Definition Serializer.h:176
int getLength() const
Definition Serializer.h:207
void const * data() const noexcept
Definition Serializer.h:56
virtual LedgerMaster & getLedgerMaster()=0
T data(T... args)
T duration_cast(T... args)
T make_shared(T... args)
T max(T... args)
@ Array
array value (ordered list)
Definition json_value.h:25
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
API version numbers used in later API versions.
Definition ApiVersion.h:35
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext const &context, json::Value &result)
Looks up a ledger from a request and fills a json::Value with ledger data.
Status ledgerFromRequest(T &ledger, GRPCContext< R > const &context)
Retrieves a ledger from a gRPC request context.
Charge const kFeeMediumBurdenRpc
Charge const kFeeHeavyBurdenRpc
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
Definition Indexes.cpp:270
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ WarnRpcFieldsDeprecated
Definition ErrorCodes.h:160
@ RpcInvalidParams
Definition ErrorCodes.h:66
@ RpcTooBusy
Definition ErrorCodes.h:38
@ RpcNoPermission
Definition ErrorCodes.h:35
void addJson(json::Value &json, LedgerFill const &fill)
Given a Ledger and options, fill a json::Value with a description of the ledger.
void open(soci::session &s, BasicConfig const &config, std::string const &dbName)
Open a soci session.
Definition SociDB.cpp:92
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
uint256 getQualityNext(uint256 const &uBase)
Definition Indexes.cpp:144
LedgerEntryType
Identifiers for on-ledger objects.
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:115
void copyFrom(json::Value &to, json::Value const &from)
Copy all the keys and values from one object into another.
T dynamic_pointer_cast(T... args)
beast::Journal const j
Definition Context.h:20
Application & app
Definition Context.h:21
LedgerMaster & ledgerMaster
Definition Context.h:24
RequestType params
Definition Context.h:51
Status represents the results of an operation that might fail.
Definition Status.h:19
static constexpr Code kOK
Definition Status.h:25
T to_string(T... args)
T unexpected(T... args)