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 =
25 params.isMember(jss::ledger) || 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
40 options_ = (full ? LedgerFill::full : 0) | (expand ? LedgerFill::expand : 0) |
41 (transactions ? LedgerFill::dumpTxrp : 0) | (accounts ? LedgerFill::dumpState : 0) |
42 (binary ? LedgerFill::binary : 0) | (owner_funds ? LedgerFill::ownerFunds : 0) |
43 (queue ? LedgerFill::dumpQueue : 0);
44
45 if (full || accounts)
46 {
47 // Until some sane way to get full ledgers has been implemented,
48 // disallow retrieving all state nodes.
50 return rpcNO_PERMISSION;
51
53 {
54 return rpcTOO_BUSY;
55 }
57 }
58 if (queue)
59 {
60 if (!ledger_ || !ledger_->open())
61 {
62 // It doesn't make sense to request the queue
63 // with a non-existent or closed/validated ledger.
64 return rpcINVALID_PARAMS;
65 }
66
68 }
69
70 return Status::OK;
71}
72
73void
75{
76 if (ledger_)
77 {
78 copyFrom(value, result_);
80 }
81 else
82 {
83 auto& master = context_.app.getLedgerMaster();
84 {
85 auto& closed = value[jss::closed] = Json::objectValue;
86 addJson(closed, {*master.getClosedLedger(), &context_, 0});
87 }
88 {
89 auto& open = value[jss::open] = Json::objectValue;
90 addJson(open, {*master.getCurrentLedger(), &context_, 0});
91 }
92 }
93
95 if (context_.params.isMember(jss::type))
96 {
98 w[jss::id] = warnRPC_FIELDS_DEPRECATED;
99 w[jss::message] =
100 "Some fields from your request are deprecated. Please check the "
101 "documentation at "
102 "https://xrpl.org/docs/references/http-websocket-apis/ "
103 "and update your request. Field `type` is deprecated.";
104 }
105
106 if (warnings.size())
107 value[jss::warnings] = std::move(warnings);
108}
109
110} // namespace RPC
111
114{
115 auto begin = std::chrono::system_clock::now();
116 org::xrpl::rpc::v1::GetLedgerRequest& request = context.params;
117 org::xrpl::rpc::v1::GetLedgerResponse response;
118 grpc::Status status = grpc::Status::OK;
119
121 if (auto status = RPC::ledgerFromRequest(ledger, context))
122 {
123 grpc::Status errorStatus;
124 if (status.toErrorCode() == rpcINVALID_PARAMS)
125 {
126 errorStatus = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, status.message());
127 }
128 else
129 {
130 errorStatus = grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
131 }
132 return {response, errorStatus};
133 }
134
135 Serializer s;
136 addRaw(ledger->header(), s, true);
137
138 response.set_ledger_header(s.peekData().data(), s.getLength());
139
140 if (request.transactions())
141 {
142 try
143 {
144 for (auto& i : ledger->txs)
145 {
146 XRPL_ASSERT(i.first, "xrpl::doLedgerGrpc : non-null transaction");
147 if (request.expand())
148 {
149 auto txn = response.mutable_transactions_list()->add_transactions();
150 Serializer sTxn = i.first->getSerializer();
151 txn->set_transaction_blob(sTxn.data(), sTxn.getLength());
152 if (i.second)
153 {
154 Serializer sMeta = i.second->getSerializer();
155 txn->set_metadata_blob(sMeta.data(), sMeta.getLength());
156 }
157 }
158 else
159 {
160 auto const& hash = i.first->getTransactionID();
161 response.mutable_hashes_list()->add_hashes(hash.data(), hash.size());
162 }
163 }
164 }
165 catch (std::exception const& e)
166 {
167 JLOG(context.j.error()) << __func__ << " - Error deserializing transaction in ledger "
168 << ledger->header().seq
169 << " . skipping transaction and following transactions. You "
170 "should look into this further";
171 }
172 }
173
174 if (request.get_objects())
175 {
176 std::shared_ptr<ReadView const> parent = context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1);
177
179 if (!base)
180 {
181 grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "parent ledger not validated"};
182 return {response, errorStatus};
183 }
184
186 if (!desired)
187 {
188 grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "ledger not validated"};
189 return {response, errorStatus};
190 }
191 SHAMap::Delta differences;
192
193 int maxDifferences = std::numeric_limits<int>::max();
194
195 bool res = base->stateMap().compare(desired->stateMap(), differences, maxDifferences);
196 if (!res)
197 {
198 grpc::Status errorStatus{
199 grpc::StatusCode::RESOURCE_EXHAUSTED, "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(inDesired->size() > 0, "xrpl::doLedgerGrpc : non-empty desired");
213 obj->set_data(inDesired->data(), inDesired->size());
214 }
215 if (inBase && inDesired)
216 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::MODIFIED);
217 else if (inBase && !inDesired)
218 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
219 else
220 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
221 auto const blob = inDesired ? inDesired->slice() : inBase->slice();
222 auto const objectType = static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
223
224 if (request.get_object_neighbors())
225 {
226 if (!(inBase && inDesired))
227 {
228 auto lb = desired->stateMap().lower_bound(k);
229 auto ub = desired->stateMap().upper_bound(k);
230 if (lb != desired->stateMap().end())
231 obj->set_predecessor(lb->key().data(), lb->key().size());
232 if (ub != desired->stateMap().end())
233 obj->set_successor(ub->key().data(), ub->key().size());
234 if (objectType == ltDIR_NODE)
235 {
236 auto sle = std::make_shared<SLE>(SerialIter{blob}, k);
237 if (!sle->isFieldPresent(sfOwner))
238 {
239 auto bookBase = keylet::quality({ltDIR_NODE, k}, 0);
240 if (!inBase && inDesired)
241 {
242 auto firstBook = desired->stateMap().upper_bound(bookBase.key);
243 if (firstBook != desired->stateMap().end() &&
244 firstBook->key() < getQualityNext(bookBase.key) && firstBook->key() == k)
245 {
246 auto succ = response.add_book_successors();
247 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
248 succ->set_first_book(firstBook->key().data(), firstBook->key().size());
249 }
250 }
251 if (inBase && !inDesired)
252 {
253 auto oldFirstBook = base->stateMap().upper_bound(bookBase.key);
254 if (oldFirstBook != base->stateMap().end() &&
255 oldFirstBook->key() < getQualityNext(bookBase.key) && oldFirstBook->key() == k)
256 {
257 auto succ = response.add_book_successors();
258 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
259 auto newFirstBook = desired->stateMap().upper_bound(bookBase.key);
260
261 if (newFirstBook != desired->stateMap().end() &&
262 newFirstBook->key() < getQualityNext(bookBase.key))
263 {
264 succ->set_first_book(newFirstBook->key().data(), newFirstBook->key().size());
265 }
266 }
267 }
268 }
269 }
270 }
271 }
272 }
273 response.set_objects_included(true);
274 response.set_object_neighbors_included(request.get_object_neighbors());
275 response.set_skiplist_included(true);
276 }
277
278 response.set_validated(context.ledgerMaster.isValidated(*ledger));
279
281 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() * 1.0;
282 JLOG(context.j.warn()) << __func__ << " - Extract time = " << duration
283 << " - num objects = " << response.ledger_objects().objects_size()
284 << " - num txns = " << response.transactions_list().transactions_size() << " - ms per obj "
285 << duration / response.ledger_objects().objects_size() << " - ms per txn "
286 << duration / response.transactions_list().transactions_size();
287
288 return {response, status};
289}
290} // 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:319
Stream warn() const
Definition Journal.h:313
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:177
int getLength() const
Definition Serializer.h:208
void const * data() const noexcept
Definition Serializer.h:57
virtual TxQ & getTxQ()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual LedgerMaster & getLedgerMaster()=0
std::vector< TxDetails > getTxs() const
Returns information about all transactions currently in the queue.
Definition TxQ.cpp:1665
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:241
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:119
@ 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:96
@ rpcTOO_BUSY
Definition ErrorCodes.h:37
@ rpcNO_PERMISSION
Definition ErrorCodes.h:34
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:65
@ warnRPC_FIELDS_DEPRECATED
Definition ErrorCodes.h:157
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