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/basics/base_uint.h>
13#include <xrpl/beast/utility/instrumentation.h>
14#include <xrpl/json/json_value.h>
15#include <xrpl/protocol/ErrorCodes.h>
16#include <xrpl/protocol/Indexes.h>
17#include <xrpl/protocol/LedgerFormats.h>
18#include <xrpl/protocol/LedgerHeader.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/Serializer.h>
21#include <xrpl/protocol/jss.h>
22#include <xrpl/resource/Fees.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#include <rpcspec/Errors.hpp>
28
29#include <chrono>
30#include <exception>
31#include <expected>
32#include <limits>
33#include <memory>
34#include <string>
35#include <utility>
36
37namespace xrpl {
38namespace rpc {
39
43
44std::expected<LedgerHandler::Output, ::rpc::Status>
45LedgerHandler::process(Input const& input)
46{
47 Output output;
48 output.options = (input.full ? static_cast<int>(LedgerFill::Options::Full) : 0) |
49 (input.expand ? static_cast<int>(LedgerFill::Options::Expand) : 0) |
50 (input.transactions ? static_cast<int>(LedgerFill::Options::DumpTxrp) : 0) |
51 (input.accounts ? static_cast<int>(LedgerFill::Options::DumpState) : 0) |
52 (input.binary ? static_cast<int>(LedgerFill::Options::Binary) : 0) |
53 (input.ownerFunds ? static_cast<int>(LedgerFill::Options::OwnerFunds) : 0) |
54 (input.queue ? static_cast<int>(LedgerFill::Options::DumpQueue) : 0);
55
56 if (input.ledger.isUnspecified())
57 return output;
58
59 if (auto const status = getLedger(output.ledger, input.ledger, context_.get()))
60 return std::unexpected{::rpc::Status{status.toErrorCode(), status.message()}};
61
62 if (input.full || input.accounts)
63 {
64 // Until some sane way to get full ledgers has been implemented,
65 // disallow retrieving all state nodes.
66 if (!isUnlimited(context_.get().role))
68
69 context_.get().loadType =
71 }
72
73 if (input.queue)
74 {
75 if (!output.ledger || !output.ledger->open())
76 {
77 // It doesn't make sense to request the queue
78 // with a non-existent or closed/validated ledger.
80 }
81
82 output.queueTxs = context_.get().app.getTxQ().getTxs();
83 }
84
85 return output;
86}
87
88void
89LedgerHandler::writeResult(json::Value& value, Output const& output) const
90{
91 if (output.ledger)
92 {
93 auto const& header = output.ledger->header();
94
95 if (output.ledger->open())
96 {
97 value[jss::ledger_current_index] = header.seq;
98 }
99 else
100 {
101 value[jss::ledger_hash] = to_string(header.hash);
102 value[jss::ledger_index] = header.seq;
103 }
104
105 value[jss::validated] = context_.get().ledgerMaster.isValidated(*output.ledger);
106
107 addJson(value, {*output.ledger, &context_.get(), output.options, output.queueTxs});
108 }
109 else
110 {
111 auto& master = context_.get().app.getLedgerMaster();
112 {
113 auto& closed = value[jss::closed] = json::ValueType::Object;
114 addJson(closed, {*master.getClosedLedger(), &context_.get(), 0});
115 }
116 {
117 auto& open = value[jss::open] = json::ValueType::Object;
118 addJson(open, {*master.getCurrentLedger(), &context_.get(), 0});
119 }
120 }
121}
122
123} // namespace rpc
124
127{
128 auto begin = std::chrono::system_clock::now();
129 org::xrpl::rpc::v1::GetLedgerRequest const& request = context.params;
130 org::xrpl::rpc::v1::GetLedgerResponse response;
131 grpc::Status const status = grpc::Status::OK;
132
134 if (auto status = rpc::ledgerFromRequest(ledger, context))
135 {
136 grpc::Status errorStatus;
137 if (status.toErrorCode() == RpcInvalidParams)
138 {
139 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
140 }
141 else
142 {
143 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
144 }
145 return {response, errorStatus};
146 }
147
148 Serializer s;
149 addRaw(ledger->header(), s, true);
150
151 response.set_ledger_header(s.peekData().data(), s.getLength());
152
153 if (request.transactions())
154 {
155 try
156 {
157 for (auto& i : ledger->txs)
158 {
159 XRPL_ASSERT(i.first, "xrpl::doLedgerGrpc : non-null transaction");
160 if (request.expand())
161 {
162 auto txn = response.mutable_transactions_list()->add_transactions();
163 Serializer const sTxn = i.first->getSerializer();
164 txn->set_transaction_blob(sTxn.data(), sTxn.getLength());
165 if (i.second)
166 {
167 Serializer const sMeta = i.second->getSerializer();
168 txn->set_metadata_blob(sMeta.data(), sMeta.getLength());
169 }
170 }
171 else
172 {
173 auto const& hash = i.first->getTransactionID();
174 response.mutable_hashes_list()->add_hashes(hash.data(), hash.size());
175 }
176 }
177 }
178 catch (std::exception const& e)
179 {
180 JLOG(context.j.error()) << __func__ << " - Error deserializing transaction in ledger "
181 << ledger->header().seq
182 << " . skipping transaction and following transactions. You "
183 "should look into this further";
184 }
185 }
186
187 if (request.get_objects())
188 {
190 context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1);
191
193 if (!base)
194 {
195 grpc::Status const errorStatus{
196 grpc::StatusCode::NOT_FOUND, "parent ledger not validated"};
197 return {response, errorStatus};
198 }
199
200 std::shared_ptr<Ledger const> const desired =
202 if (!desired)
203 {
204 grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "ledger not validated"};
205 return {response, errorStatus};
206 }
207 SHAMap::Delta differences;
208
209 int const maxDifferences = std::numeric_limits<int>::max();
210
211 bool const res = base->stateMap().compare(desired->stateMap(), differences, maxDifferences);
212 if (!res)
213 {
214 grpc::Status const errorStatus{
215 grpc::StatusCode::RESOURCE_EXHAUSTED,
216 "too many differences between specified ledgers"};
217 return {response, errorStatus};
218 }
219
220 for (auto& [k, v] : differences)
221 {
222 auto obj = response.mutable_ledger_objects()->add_objects();
223 auto inBase = v.first;
224 auto inDesired = v.second;
225
226 obj->set_key(k.data(), k.size());
227 if (inDesired)
228 {
229 XRPL_ASSERT(inDesired->size() > 0, "xrpl::doLedgerGrpc : non-empty desired");
230 obj->set_data(inDesired->data(), inDesired->size());
231 }
232 if (inBase && inDesired)
233 {
234 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::MODIFIED);
235 }
236 else if (inBase && !inDesired)
237 {
238 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
239 }
240 else
241 {
242 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
243 }
244 auto const blob = inDesired ? inDesired->slice() : inBase->slice();
245 auto const objectType = static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
246
247 if (request.get_object_neighbors())
248 {
249 if (!(inBase && inDesired))
250 {
251 auto lb = desired->stateMap().lowerBound(k);
252 auto ub = desired->stateMap().upperBound(k);
253 if (lb != desired->stateMap().end())
254 obj->set_predecessor(lb->key().data(), lb->key().size());
255 if (ub != desired->stateMap().end())
256 obj->set_successor(ub->key().data(), ub->key().size());
257 if (objectType == ltDIR_NODE)
258 {
259 auto sle = std::make_shared<SLE>(SerialIter{blob}, k);
260 if (!sle->isFieldPresent(sfOwner))
261 {
262 auto bookBase = keylet::quality({ltDIR_NODE, k}, 0);
263 if (!inBase && inDesired)
264 {
265 auto firstBook = desired->stateMap().upperBound(bookBase.key);
266 if (firstBook != desired->stateMap().end() &&
267 firstBook->key() < getQualityNext(bookBase.key) &&
268 firstBook->key() == k)
269 {
270 auto succ = response.add_book_successors();
271 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
272 succ->set_first_book(
273 firstBook->key().data(), firstBook->key().size());
274 }
275 }
276 if (inBase && !inDesired)
277 {
278 auto oldFirstBook = base->stateMap().upperBound(bookBase.key);
279 if (oldFirstBook != base->stateMap().end() &&
280 oldFirstBook->key() < getQualityNext(bookBase.key) &&
281 oldFirstBook->key() == k)
282 {
283 auto succ = response.add_book_successors();
284 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
285 auto newFirstBook =
286 desired->stateMap().upperBound(bookBase.key);
287
288 if (newFirstBook != desired->stateMap().end() &&
289 newFirstBook->key() < getQualityNext(bookBase.key))
290 {
291 succ->set_first_book(
292 newFirstBook->key().data(), newFirstBook->key().size());
293 }
294 }
295 }
296 }
297 }
298 }
299 }
300 }
301 response.set_objects_included(true);
302 response.set_object_neighbors_included(request.get_object_neighbors());
303 response.set_skiplist_included(true);
304 }
305
306 response.set_validated(context.ledgerMaster.isValidated(*ledger));
307
309 auto duration =
311 // Guard the per-item rates: an empty ledger has zero objects and/or zero
312 // transactions, and dividing by zero is undefined for these doubles.
313 auto const numObjects = response.ledger_objects().objects_size();
314 auto const numTxns = response.transactions_list().transactions_size();
315 std::string const msPerObj = numObjects > 0 ? std::to_string(duration / numObjects) : "n/a";
316 std::string const msPerTxn = numTxns > 0 ? std::to_string(duration / numTxns) : "n/a";
317 JLOG(context.j.warn()) << __func__ << " - Extract time = " << duration
318 << " - num objects = " << numObjects << " - num txns = " << numTxns
319 << " - ms per obj " << msPerObj << " - ms per txn " << msPerTxn;
320
321 return {response, status};
322}
323} // namespace xrpl
Stream error() const
Definition Journal.h:362
Stream warn() const
Definition Journal.h:356
Represents a JSON value.
Definition json_value.h:117
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
Finds a ledger by sequence, preferring the validated chain.
bool isValidated(ReadView const &ledger)
Tests whether a ledger is on the validated chain, using the skip list when the ledger does not alread...
std::map< UInt256, DeltaItem > Delta
Definition SHAMap.h:148
Blob const & peekData() const
Definition Serializer.h:273
int getLength() const
Definition Serializer.h:304
void const * data() const noexcept
Definition Serializer.h:153
virtual LedgerMaster & getLedgerMaster()=0
std::expected< Output, ::rpc::Status > process(Input const &input)
void writeResult(json::Value &value, Output const &output) const
std::reference_wrapper< JsonContext > context_
T data(T... args)
T duration_cast(T... args)
T make_shared(T... args)
T max(T... args)
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
Keylet quality(Keylet const &k, std::uint64_t const q) noexcept
The initial directory page for a specific quality.
Definition Indexes.cpp:304
Charge const kFeeHeavyBurdenRpc
Charge const kFeeMediumBurdenRpc
API version numbers used in later API versions.
Definition ApiVersion.h:36
Status getLedger(T &ledger, UInt256 const &ledgerHash, Context const &context)
Retrieves a ledger by its hash.
Status ledgerFromRequest(T &ledger, GRPCContext< R > const &context)
Retrieves a ledger from a gRPC request context.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ RpcInvalidParams
Definition ErrorCodes.h:67
@ RpcNoPermission
Definition ErrorCodes.h:36
void addJson(json::Value &json, LedgerFill const &fill)
Given a Ledger and options, fill a json::Value with a description of the ledger.
UInt256 getQualityNext(UInt256 const &uBase)
Definition Indexes.cpp:186
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
void open(soci::session &s, BasicConfig const &config, std::string const &dbName)
Open a soci session.
Definition SociDB.cpp:91
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(rpc::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
LedgerEntryType
Identifiers for on-ledger objects.
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:115
T dynamic_pointer_cast(T... args)
beast::Journal const j
Definition Context.h:28
Application & app
Definition Context.h:29
LedgerMaster & ledgerMaster
Definition Context.h:32
RequestType params
Definition Context.h:59
Status represents the results of an operation that might fail.
Definition Status.h:27
T to_string(T... args)
T unexpected(T... args)