rippled
Loading...
Searching...
No Matches
xrpld/rpc/handlers/ledger/Ledger.cpp
1#include <xrpld/app/ledger/LedgerToJson.h>
2#include <xrpld/app/main/Application.h>
3#include <xrpld/rpc/GRPCHandlers.h>
4#include <xrpld/rpc/Role.h>
5#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
6#include <xrpld/rpc/handlers/ledger/Ledger.h>
7
8#include <xrpl/protocol/ErrorCodes.h>
9#include <xrpl/protocol/jss.h>
10#include <xrpl/resource/Fees.h>
11#include <xrpl/server/LoadFeeTrack.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 const needsLedger = params.isMember(jss::ledger) || params.isMember(jss::ledger_hash) ||
25 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() != 0u)
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 const& request = context.params;
117 org::xrpl::rpc::v1::GetLedgerResponse response;
118 grpc::Status const 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 const sTxn = i.first->getSerializer();
151 txn->set_transaction_blob(sTxn.data(), sTxn.getLength());
152 if (i.second)
153 {
154 Serializer const 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 {
177 context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1);
178
180 if (!base)
181 {
182 grpc::Status const errorStatus{
183 grpc::StatusCode::NOT_FOUND, "parent ledger not validated"};
184 return {response, errorStatus};
185 }
186
187 std::shared_ptr<Ledger const> const desired =
189 if (!desired)
190 {
191 grpc::Status const errorStatus{grpc::StatusCode::NOT_FOUND, "ledger not validated"};
192 return {response, errorStatus};
193 }
194 SHAMap::Delta differences;
195
196 int const maxDifferences = std::numeric_limits<int>::max();
197
198 bool const res = base->stateMap().compare(desired->stateMap(), differences, maxDifferences);
199 if (!res)
200 {
201 grpc::Status const errorStatus{
202 grpc::StatusCode::RESOURCE_EXHAUSTED,
203 "too many differences between specified ledgers"};
204 return {response, errorStatus};
205 }
206
207 for (auto& [k, v] : differences)
208 {
209 auto obj = response.mutable_ledger_objects()->add_objects();
210 auto inBase = v.first;
211 auto inDesired = v.second;
212
213 obj->set_key(k.data(), k.size());
214 if (inDesired)
215 {
216 XRPL_ASSERT(inDesired->size() > 0, "xrpl::doLedgerGrpc : non-empty desired");
217 obj->set_data(inDesired->data(), inDesired->size());
218 }
219 if (inBase && inDesired)
220 {
221 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::MODIFIED);
222 }
223 else if (inBase && !inDesired)
224 {
225 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
226 }
227 else
228 {
229 obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
230 }
231 auto const blob = inDesired ? inDesired->slice() : inBase->slice();
232 auto const objectType = static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
233
234 if (request.get_object_neighbors())
235 {
236 if (!(inBase && inDesired))
237 {
238 auto lb = desired->stateMap().lower_bound(k);
239 auto ub = desired->stateMap().upper_bound(k);
240 if (lb != desired->stateMap().end())
241 obj->set_predecessor(lb->key().data(), lb->key().size());
242 if (ub != desired->stateMap().end())
243 obj->set_successor(ub->key().data(), ub->key().size());
244 if (objectType == ltDIR_NODE)
245 {
246 auto sle = std::make_shared<SLE>(SerialIter{blob}, k);
247 if (!sle->isFieldPresent(sfOwner))
248 {
249 auto bookBase = keylet::quality({ltDIR_NODE, k}, 0);
250 if (!inBase && inDesired)
251 {
252 auto firstBook = desired->stateMap().upper_bound(bookBase.key);
253 if (firstBook != desired->stateMap().end() &&
254 firstBook->key() < getQualityNext(bookBase.key) &&
255 firstBook->key() == k)
256 {
257 auto succ = response.add_book_successors();
258 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
259 succ->set_first_book(
260 firstBook->key().data(), firstBook->key().size());
261 }
262 }
263 if (inBase && !inDesired)
264 {
265 auto oldFirstBook = base->stateMap().upper_bound(bookBase.key);
266 if (oldFirstBook != base->stateMap().end() &&
267 oldFirstBook->key() < getQualityNext(bookBase.key) &&
268 oldFirstBook->key() == k)
269 {
270 auto succ = response.add_book_successors();
271 succ->set_book_base(bookBase.key.data(), bookBase.key.size());
272 auto newFirstBook =
273 desired->stateMap().upper_bound(bookBase.key);
274
275 if (newFirstBook != desired->stateMap().end() &&
276 newFirstBook->key() < getQualityNext(bookBase.key))
277 {
278 succ->set_first_book(
279 newFirstBook->key().data(), newFirstBook->key().size());
280 }
281 }
282 }
283 }
284 }
285 }
286 }
287 }
288 response.set_objects_included(true);
289 response.set_object_neighbors_included(request.get_object_neighbors());
290 response.set_skiplist_included(true);
291 }
292
293 response.set_validated(context.ledgerMaster.isValidated(*ledger));
294
296 auto duration =
297 std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() * 1.0;
298 JLOG(context.j.warn()) << __func__ << " - Extract time = " << duration
299 << " - num objects = " << response.ledger_objects().objects_size()
300 << " - num txns = " << response.transactions_list().transactions_size()
301 << " - ms per obj "
302 << duration / response.ledger_objects().objects_size()
303 << " - ms per txn "
304 << duration / response.transactions_list().transactions_size();
305
306 return {response, status};
307}
308} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
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
std::shared_ptr< ReadView const > ledger_
Blob const & peekData() const
Definition Serializer.h:176
int getLength() const
Definition Serializer.h:207
void const * data() const noexcept
Definition Serializer.h:56
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:1755
T data(T... args)
T is_same_v
T max(T... args)
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
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:249
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
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:98
@ rpcTOO_BUSY
Definition ErrorCodes.h:36
@ rpcNO_PERMISSION
Definition ErrorCodes.h:33
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
@ warnRPC_FIELDS_DEPRECATED
Definition ErrorCodes.h:156
beast::Journal const j
Definition Context.h:20
Application & app
Definition Context.h:21
Resource::Charge & loadType
Definition Context.h:22
LedgerMaster & ledgerMaster
Definition Context.h:24
RequestType params
Definition Context.h:51
Json::Value params
Definition Context.h:43
Status represents the results of an operation that might fail.
Definition Status.h:20
static constexpr Code OK
Definition Status.h:26