rippled
Loading...
Searching...
No Matches
LedgerHandler.cpp
1#include <xrpld/app/ledger/LedgerToJson.h>
2#include <xrpld/app/main/Application.h>
3#include <xrpld/app/misc/LoadFeeTrack.h>
4#include <xrpld/rpc/GRPCHandlers.h>
5#include <xrpld/rpc/Role.h>
6#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
7#include <xrpld/rpc/handlers/LedgerHandler.h>
8
9#include <xrpl/protocol/ErrorCodes.h>
10#include <xrpl/protocol/jss.h>
11#include <xrpl/resource/Fees.h>
12
13namespace xrpl {
14namespace RPC {
15
16LedgerHandler::LedgerHandler(JsonContext& context) : context_(context)
17{
18}
19
22{
23 auto const& params = context_.params;
24 bool needsLedger = params.isMember(jss::ledger) ||
25 params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index);
26 if (!needsLedger)
27 return Status::OK;
28
29 if (auto s = lookupLedger(ledger_, context_, result_))
30 return s;
31
32 bool const full = params[jss::full].asBool();
33 bool const transactions = params[jss::transactions].asBool();
34 bool const accounts = params[jss::accounts].asBool();
35 bool const expand = params[jss::expand].asBool();
36 bool const binary = params[jss::binary].asBool();
37 bool const owner_funds = params[jss::owner_funds].asBool();
38 bool const queue = params[jss::queue].asBool();
39
41 (expand ? LedgerFill::expand : 0) |
42 (transactions ? LedgerFill::dumpTxrp : 0) |
43 (accounts ? LedgerFill::dumpState : 0) |
44 (binary ? LedgerFill::binary : 0) |
45 (owner_funds ? LedgerFill::ownerFunds : 0) |
46 (queue ? LedgerFill::dumpQueue : 0);
47
48 if (full || accounts)
49 {
50 // Until some sane way to get full ledgers has been implemented,
51 // disallow retrieving all state nodes.
53 return rpcNO_PERMISSION;
54
57 {
58 return rpcTOO_BUSY;
59 }
62 }
63 if (queue)
64 {
65 if (!ledger_ || !ledger_->open())
66 {
67 // It doesn't make sense to request the queue
68 // with a non-existent or closed/validated ledger.
69 return rpcINVALID_PARAMS;
70 }
71
73 }
74
75 return Status::OK;
76}
77
78void
80{
81 if (ledger_)
82 {
83 copyFrom(value, result_);
85 }
86 else
87 {
88 auto& master = context_.app.getLedgerMaster();
89 {
90 auto& closed = value[jss::closed] = Json::objectValue;
91 addJson(closed, {*master.getClosedLedger(), &context_, 0});
92 }
93 {
94 auto& open = value[jss::open] = Json::objectValue;
95 addJson(open, {*master.getCurrentLedger(), &context_, 0});
96 }
97 }
98
100 if (context_.params.isMember(jss::type))
101 {
102 Json::Value& w = warnings.append(Json::objectValue);
103 w[jss::id] = warnRPC_FIELDS_DEPRECATED;
104 w[jss::message] =
105 "Some fields from your request are deprecated. Please check the "
106 "documentation at "
107 "https://xrpl.org/docs/references/http-websocket-apis/ "
108 "and update your request. Field `type` is deprecated.";
109 }
110
111 if (warnings.size())
112 value[jss::warnings] = std::move(warnings);
113}
114
115} // namespace RPC
116
119{
120 auto begin = std::chrono::system_clock::now();
121 org::xrpl::rpc::v1::GetLedgerRequest& request = context.params;
122 org::xrpl::rpc::v1::GetLedgerResponse response;
123 grpc::Status status = grpc::Status::OK;
124
126 if (auto status = RPC::ledgerFromRequest(ledger, context))
127 {
128 grpc::Status errorStatus;
129 if (status.toErrorCode() == rpcINVALID_PARAMS)
130 {
131 errorStatus = grpc::Status(
132 grpc::StatusCode::INVALID_ARGUMENT, status.message());
133 }
134 else
135 {
136 errorStatus =
137 grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
138 }
139 return {response, errorStatus};
140 }
141
142 Serializer s;
143 addRaw(ledger->header(), s, true);
144
145 response.set_ledger_header(s.peekData().data(), s.getLength());
146
147 if (request.transactions())
148 {
149 try
150 {
151 for (auto& i : ledger->txs)
152 {
153 XRPL_ASSERT(
154 i.first, "xrpl::doLedgerGrpc : non-null transaction");
155 if (request.expand())
156 {
157 auto txn = response.mutable_transactions_list()
158 ->add_transactions();
159 Serializer sTxn = i.first->getSerializer();
160 txn->set_transaction_blob(sTxn.data(), sTxn.getLength());
161 if (i.second)
162 {
163 Serializer sMeta = i.second->getSerializer();
164 txn->set_metadata_blob(sMeta.data(), sMeta.getLength());
165 }
166 }
167 else
168 {
169 auto const& hash = i.first->getTransactionID();
170 response.mutable_hashes_list()->add_hashes(
171 hash.data(), hash.size());
172 }
173 }
174 }
175 catch (std::exception const& e)
176 {
177 JLOG(context.j.error())
178 << __func__ << " - Error deserializing transaction in ledger "
179 << ledger->header().seq
180 << " . skipping transaction and following transactions. You "
181 "should look into this further";
182 }
183 }
184
185 if (request.get_objects())
186 {
188 context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1);
189
192 if (!base)
193 {
194 grpc::Status errorStatus{
195 grpc::StatusCode::NOT_FOUND, "parent ledger not validated"};
196 return {response, errorStatus};
197 }
198
201 if (!desired)
202 {
203 grpc::Status errorStatus{
204 grpc::StatusCode::NOT_FOUND, "ledger not validated"};
205 return {response, errorStatus};
206 }
207 SHAMap::Delta differences;
208
209 int maxDifferences = std::numeric_limits<int>::max();
210
211 bool res = base->stateMap().compare(
212 desired->stateMap(), differences, maxDifferences);
213 if (!res)
214 {
215 grpc::Status errorStatus{
216 grpc::StatusCode::RESOURCE_EXHAUSTED,
217 "too many differences between specified ledgers"};
218 return {response, errorStatus};
219 }
220
221 for (auto& [k, v] : differences)
222 {
223 auto obj = response.mutable_ledger_objects()->add_objects();
224 auto inBase = v.first;
225 auto inDesired = v.second;
226
227 obj->set_key(k.data(), k.size());
228 if (inDesired)
229 {
230 XRPL_ASSERT(
231 inDesired->size() > 0,
232 "xrpl::doLedgerGrpc : non-empty desired");
233 obj->set_data(inDesired->data(), inDesired->size());
234 }
235 if (inBase && inDesired)
236 obj->set_mod_type(
237 org::xrpl::rpc::v1::RawLedgerObject::MODIFIED);
238 else if (inBase && !inDesired)
239 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
240 else
241 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
242 auto const blob = inDesired ? inDesired->slice() : inBase->slice();
243 auto const objectType =
244 static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
245
246 if (request.get_object_neighbors())
247 {
248 if (!(inBase && inDesired))
249 {
250 auto lb = desired->stateMap().lower_bound(k);
251 auto ub = desired->stateMap().upper_bound(k);
252 if (lb != desired->stateMap().end())
253 obj->set_predecessor(
254 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 =
266 desired->stateMap().upper_bound(
267 bookBase.key);
268 if (firstBook != desired->stateMap().end() &&
269 firstBook->key() <
270 getQualityNext(bookBase.key) &&
271 firstBook->key() == k)
272 {
273 auto succ = response.add_book_successors();
274 succ->set_book_base(
275 bookBase.key.data(),
276 bookBase.key.size());
277 succ->set_first_book(
278 firstBook->key().data(),
279 firstBook->key().size());
280 }
281 }
282 if (inBase && !inDesired)
283 {
284 auto oldFirstBook =
285 base->stateMap().upper_bound(bookBase.key);
286 if (oldFirstBook != base->stateMap().end() &&
287 oldFirstBook->key() <
288 getQualityNext(bookBase.key) &&
289 oldFirstBook->key() == k)
290 {
291 auto succ = response.add_book_successors();
292 succ->set_book_base(
293 bookBase.key.data(),
294 bookBase.key.size());
295 auto newFirstBook =
296 desired->stateMap().upper_bound(
297 bookBase.key);
298
299 if (newFirstBook !=
300 desired->stateMap().end() &&
301 newFirstBook->key() <
302 getQualityNext(bookBase.key))
303 {
304 succ->set_first_book(
305 newFirstBook->key().data(),
306 newFirstBook->key().size());
307 }
308 }
309 }
310 }
311 }
312 }
313 }
314 }
315 response.set_objects_included(true);
316 response.set_object_neighbors_included(request.get_object_neighbors());
317 response.set_skiplist_included(true);
318 }
319
320 response.set_validated(context.ledgerMaster.isValidated(*ledger));
321
323 auto duration =
324 std::chrono::duration_cast<std::chrono::milliseconds>(end - begin)
325 .count() *
326 1.0;
327 JLOG(context.j.warn())
328 << __func__ << " - Extract time = " << duration
329 << " - num objects = " << response.ledger_objects().objects_size()
330 << " - num txns = " << response.transactions_list().transactions_size()
331 << " - ms per obj "
332 << duration / response.ledger_objects().objects_size()
333 << " - ms per txn "
334 << duration / response.transactions_list().transactions_size();
335
336 return {response, status};
337}
338} // namespace xrpl
Represents a JSON value.
Definition json_value.h:131
Value & append(Value const &value)
Append value to array at the end.
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream error() const
Definition Journal.h:327
Stream warn() const
Definition Journal.h:321
virtual TxQ & getTxQ()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual LedgerMaster & getLedgerMaster()=0
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
bool isValidated(ReadView const &ledger)
bool isLoadedLocal() const
void writeResult(Json::Value &)
std::shared_ptr< ReadView const > ledger_
std::vector< TxQ::TxDetails > queueTxs_
Blob const & peekData() const
Definition Serializer.h:183
int getLength() const
Definition Serializer.h:214
void const * data() const noexcept
Definition Serializer.h:59
std::vector< TxDetails > getTxs() const
Returns information about all transactions currently in the queue.
Definition TxQ.cpp:1823
T data(T... args)
T is_same_v
T max(T... args)
@ arrayValue
array value (ordered list)
Definition json_value.h:26
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
Status ledgerFromRequest(T &ledger, GRPCContext< R > const &context)
Retrieves a ledger from a gRPC request context.
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.
Charge const feeMediumBurdenRPC
Charge const feeHeavyBurdenRPC
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
Definition Indexes.cpp:262
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
void copyFrom(Json::Value &to, Json::Value const &from)
Copy all the keys and values from one object into another.
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
uint256 getQualityNext(uint256 const &uBase)
Definition Indexes.cpp:123
@ open
We haven't closed our ledger yet, but others might have.
void addJson(Json::Value &json, LedgerFill const &fill)
Given a Ledger and options, fill a Json::Value with a description of the ledger.
LedgerEntryType
Identifiers for on-ledger objects.
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:106
@ rpcTOO_BUSY
Definition ErrorCodes.h:37
@ rpcNO_PERMISSION
Definition ErrorCodes.h:34
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:65
@ warnRPC_FIELDS_DEPRECATED
Definition ErrorCodes.h:158
beast::Journal const j
Definition Context.h:21
Application & app
Definition Context.h:22
Resource::Charge & loadType
Definition Context.h:23
LedgerMaster & ledgerMaster
Definition Context.h:25
RequestType params
Definition Context.h:52
Json::Value params
Definition Context.h:44
Status represents the results of an operation that might fail.
Definition Status.h:21
static constexpr Code OK
Definition Status.h:27