rippled
Loading...
Searching...
No Matches
LedgerHandler.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2014 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or 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#include <xrpld/app/ledger/LedgerToJson.h>
21#include <xrpld/app/main/Application.h>
22#include <xrpld/app/misc/LoadFeeTrack.h>
23#include <xrpld/rpc/GRPCHandlers.h>
24#include <xrpld/rpc/Role.h>
25#include <xrpld/rpc/handlers/LedgerHandler.h>
26#include <xrpl/json/Object.h>
27#include <xrpl/protocol/ErrorCodes.h>
28#include <xrpl/protocol/jss.h>
29#include <xrpl/resource/Fees.h>
30
31namespace ripple {
32namespace RPC {
33
34LedgerHandler::LedgerHandler(JsonContext& context) : context_(context)
35{
36}
37
40{
41 auto const& params = context_.params;
42 bool needsLedger = params.isMember(jss::ledger) ||
43 params.isMember(jss::ledger_hash) || params.isMember(jss::ledger_index);
44 if (!needsLedger)
45 return Status::OK;
46
47 if (auto s = lookupLedger(ledger_, context_, result_))
48 return s;
49
50 bool const full = params[jss::full].asBool();
51 bool const transactions = params[jss::transactions].asBool();
52 bool const accounts = params[jss::accounts].asBool();
53 bool const expand = params[jss::expand].asBool();
54 bool const binary = params[jss::binary].asBool();
55 bool const owner_funds = params[jss::owner_funds].asBool();
56 bool const queue = params[jss::queue].asBool();
57 auto type = chooseLedgerEntryType(params);
58 if (type.first)
59 return type.first;
60 type_ = type.second;
61
62 options_ = (full ? LedgerFill::full : 0) |
63 (expand ? LedgerFill::expand : 0) |
64 (transactions ? LedgerFill::dumpTxrp : 0) |
65 (accounts ? LedgerFill::dumpState : 0) |
66 (binary ? LedgerFill::binary : 0) |
67 (owner_funds ? LedgerFill::ownerFunds : 0) |
68 (queue ? LedgerFill::dumpQueue : 0);
69
70 if (full || accounts)
71 {
72 // Until some sane way to get full ledgers has been implemented,
73 // disallow retrieving all state nodes.
75 return rpcNO_PERMISSION;
76
79 {
80 return rpcTOO_BUSY;
81 }
84 }
85 if (queue)
86 {
87 if (!ledger_ || !ledger_->open())
88 {
89 // It doesn't make sense to request the queue
90 // with a non-existent or closed/validated ledger.
91 return rpcINVALID_PARAMS;
92 }
93
95 }
96
97 return Status::OK;
98}
99
100} // namespace RPC
101
104{
105 auto begin = std::chrono::system_clock::now();
106 org::xrpl::rpc::v1::GetLedgerRequest& request = context.params;
107 org::xrpl::rpc::v1::GetLedgerResponse response;
108 grpc::Status status = grpc::Status::OK;
109
111 if (auto status = RPC::ledgerFromRequest(ledger, context))
112 {
113 grpc::Status errorStatus;
114 if (status.toErrorCode() == rpcINVALID_PARAMS)
115 {
116 errorStatus = grpc::Status(
117 grpc::StatusCode::INVALID_ARGUMENT, status.message());
118 }
119 else
120 {
121 errorStatus =
122 grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
123 }
124 return {response, errorStatus};
125 }
126
127 Serializer s;
128 addRaw(ledger->info(), s, true);
129
130 response.set_ledger_header(s.peekData().data(), s.getLength());
131
132 if (request.transactions())
133 {
134 try
135 {
136 for (auto& i : ledger->txs)
137 {
138 XRPL_ASSERT(
139 i.first, "ripple::doLedgerGrpc : non-null transaction");
140 if (request.expand())
141 {
142 auto txn = response.mutable_transactions_list()
143 ->add_transactions();
144 Serializer sTxn = i.first->getSerializer();
145 txn->set_transaction_blob(sTxn.data(), sTxn.getLength());
146 if (i.second)
147 {
148 Serializer sMeta = i.second->getSerializer();
149 txn->set_metadata_blob(sMeta.data(), sMeta.getLength());
150 }
151 }
152 else
153 {
154 auto const& hash = i.first->getTransactionID();
155 response.mutable_hashes_list()->add_hashes(
156 hash.data(), hash.size());
157 }
158 }
159 }
160 catch (std::exception const& e)
161 {
162 JLOG(context.j.error())
163 << __func__ << " - Error deserializing transaction in ledger "
164 << ledger->info().seq
165 << " . skipping transaction and following transactions. You "
166 "should look into this further";
167 }
168 }
169
170 if (request.get_objects())
171 {
173 context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1);
174
176 std::dynamic_pointer_cast<Ledger const>(parent);
177 if (!base)
178 {
179 grpc::Status errorStatus{
180 grpc::StatusCode::NOT_FOUND, "parent ledger not validated"};
181 return {response, errorStatus};
182 }
183
185 std::dynamic_pointer_cast<Ledger const>(ledger);
186 if (!desired)
187 {
188 grpc::Status errorStatus{
189 grpc::StatusCode::NOT_FOUND, "ledger not validated"};
190 return {response, errorStatus};
191 }
192 SHAMap::Delta differences;
193
194 int maxDifferences = std::numeric_limits<int>::max();
195
196 bool res = base->stateMap().compare(
197 desired->stateMap(), differences, maxDifferences);
198 if (!res)
199 {
200 grpc::Status errorStatus{
201 grpc::StatusCode::RESOURCE_EXHAUSTED,
202 "too many differences between specified ledgers"};
203 return {response, errorStatus};
204 }
205
206 for (auto& [k, v] : differences)
207 {
208 auto obj = response.mutable_ledger_objects()->add_objects();
209 auto inBase = v.first;
210 auto inDesired = v.second;
211
212 obj->set_key(k.data(), k.size());
213 if (inDesired)
214 {
215 XRPL_ASSERT(
216 inDesired->size() > 0,
217 "ripple::doLedgerGrpc : non-empty desired");
218 obj->set_data(inDesired->data(), inDesired->size());
219 }
220 if (inBase && inDesired)
221 obj->set_mod_type(
222 org::xrpl::rpc::v1::RawLedgerObject::MODIFIED);
223 else if (inBase && !inDesired)
224 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
225 else
226 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
227 auto const blob = inDesired ? inDesired->slice() : inBase->slice();
228 auto const objectType =
229 static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
230
231 if (request.get_object_neighbors())
232 {
233 if (!(inBase && inDesired))
234 {
235 auto lb = desired->stateMap().lower_bound(k);
236 auto ub = desired->stateMap().upper_bound(k);
237 if (lb != desired->stateMap().end())
238 obj->set_predecessor(
239 lb->key().data(), lb->key().size());
240 if (ub != desired->stateMap().end())
241 obj->set_successor(ub->key().data(), ub->key().size());
242 if (objectType == ltDIR_NODE)
243 {
244 auto sle = std::make_shared<SLE>(SerialIter{blob}, k);
245 if (!sle->isFieldPresent(sfOwner))
246 {
247 auto bookBase = keylet::quality({ltDIR_NODE, k}, 0);
248 if (!inBase && inDesired)
249 {
250 auto firstBook =
251 desired->stateMap().upper_bound(
252 bookBase.key);
253 if (firstBook != desired->stateMap().end() &&
254 firstBook->key() <
255 getQualityNext(bookBase.key) &&
256 firstBook->key() == k)
257 {
258 auto succ = response.add_book_successors();
259 succ->set_book_base(
260 bookBase.key.data(),
261 bookBase.key.size());
262 succ->set_first_book(
263 firstBook->key().data(),
264 firstBook->key().size());
265 }
266 }
267 if (inBase && !inDesired)
268 {
269 auto oldFirstBook =
270 base->stateMap().upper_bound(bookBase.key);
271 if (oldFirstBook != base->stateMap().end() &&
272 oldFirstBook->key() <
273 getQualityNext(bookBase.key) &&
274 oldFirstBook->key() == k)
275 {
276 auto succ = response.add_book_successors();
277 succ->set_book_base(
278 bookBase.key.data(),
279 bookBase.key.size());
280 auto newFirstBook =
281 desired->stateMap().upper_bound(
282 bookBase.key);
283
284 if (newFirstBook !=
285 desired->stateMap().end() &&
286 newFirstBook->key() <
287 getQualityNext(bookBase.key))
288 {
289 succ->set_first_book(
290 newFirstBook->key().data(),
291 newFirstBook->key().size());
292 }
293 }
294 }
295 }
296 }
297 }
298 }
299 }
300 response.set_objects_included(true);
301 response.set_object_neighbors_included(request.get_object_neighbors());
302 response.set_skiplist_included(true);
303 }
304
305 response.set_validated(context.ledgerMaster.isValidated(*ledger));
306
308 auto duration =
309 std::chrono::duration_cast<std::chrono::milliseconds>(end - begin)
310 .count() *
311 1.0;
312 JLOG(context.j.warn())
313 << __func__ << " - Extract time = " << duration
314 << " - num objects = " << response.ledger_objects().objects_size()
315 << " - num txns = " << response.transactions_list().transactions_size()
316 << " - ms per obj "
317 << duration / response.ledger_objects().objects_size()
318 << " - ms per txn "
319 << duration / response.transactions_list().transactions_size();
320
321 return {response, status};
322}
323} // namespace ripple
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:943
Stream error() const
Definition: Journal.h:335
Stream warn() const
Definition: Journal.h:329
virtual LoadFeeTrack & getFeeTrack()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual TxQ & getTxQ()=0
bool isValidated(ReadView const &ledger)
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
bool isLoadedLocal() const
Definition: LoadFeeTrack.h:127
std::shared_ptr< ReadView const > ledger_
Definition: LedgerHandler.h:74
std::vector< TxQ::TxDetails > queueTxs_
Definition: LedgerHandler.h:75
void const * data() const noexcept
Definition: Serializer.h:78
Blob const & peekData() const
Definition: Serializer.h:202
int getLength() const
Definition: Serializer.h:233
std::vector< TxDetails > getTxs() const
Returns information about all transactions currently in the queue.
Definition: TxQ.cpp:1844
T data(T... args)
T max(T... args)
Status ledgerFromRequest(T &ledger, GRPCContext< R > &context)
Definition: RPCHelpers.cpp:409
std::pair< RPC::Status, LedgerEntryType > chooseLedgerEntryType(Json::Value const &params)
Definition: RPCHelpers.cpp:932
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext &context, Json::Value &result)
Look up a ledger from a request and fill a Json::Result with the data representing a ledger.
Definition: RPCHelpers.cpp:623
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:256
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ rpcTOO_BUSY
Definition: ErrorCodes.h:56
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
@ rpcNO_PERMISSION
Definition: ErrorCodes.h:53
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition: Role.cpp:124
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
uint256 getQualityNext(uint256 const &uBase)
Definition: Indexes.cpp:117
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:54
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
Resource::Charge & loadType
Definition: Context.h:43
Application & app
Definition: Context.h:42
beast::Journal const j
Definition: Context.h:41
LedgerMaster & ledgerMaster
Definition: Context.h:45
RequestType params
Definition: Context.h:72
Json::Value params
Definition: Context.h:64
Status represents the results of an operation that might fail.
Definition: Status.h:40
static constexpr Code OK
Definition: Status.h:46