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