rippled
LedgerEntry.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 <ripple/app/main/Application.h>
21 #include <ripple/basics/StringUtilities.h>
22 #include <ripple/basics/strHex.h>
23 #include <ripple/json/json_errors.h>
24 #include <ripple/ledger/ReadView.h>
25 #include <ripple/net/RPCErr.h>
26 #include <ripple/protocol/ErrorCodes.h>
27 #include <ripple/protocol/Indexes.h>
28 #include <ripple/protocol/jss.h>
29 #include <ripple/rpc/Context.h>
30 #include <ripple/rpc/GRPCHandlers.h>
31 #include <ripple/rpc/impl/RPCHelpers.h>
32 
33 namespace ripple {
34 
35 // {
36 // ledger_hash : <ledger>
37 // ledger_index : <ledger_index>
38 // ...
39 // }
42 {
44  auto jvResult = RPC::lookupLedger(lpLedger, context);
45 
46  if (!lpLedger)
47  return jvResult;
48 
49  uint256 uNodeIndex;
50  bool bNodeBinary = false;
51  LedgerEntryType expectedType = ltANY;
52 
53  try
54  {
55  if (context.params.isMember(jss::index))
56  {
57  if (!uNodeIndex.parseHex(context.params[jss::index].asString()))
58  {
59  uNodeIndex = beast::zero;
60  jvResult[jss::error] = "malformedRequest";
61  }
62  }
63  else if (context.params.isMember(jss::account_root))
64  {
65  expectedType = ltACCOUNT_ROOT;
66  auto const account = parseBase58<AccountID>(
67  context.params[jss::account_root].asString());
68  if (!account || account->isZero())
69  jvResult[jss::error] = "malformedAddress";
70  else
71  uNodeIndex = keylet::account(*account).key;
72  }
73  else if (context.params.isMember(jss::check))
74  {
75  expectedType = ltCHECK;
76  if (!uNodeIndex.parseHex(context.params[jss::check].asString()))
77  {
78  uNodeIndex = beast::zero;
79  jvResult[jss::error] = "malformedRequest";
80  }
81  }
82  else if (context.params.isMember(jss::deposit_preauth))
83  {
84  expectedType = ltDEPOSIT_PREAUTH;
85 
86  if (!context.params[jss::deposit_preauth].isObject())
87  {
88  if (!context.params[jss::deposit_preauth].isString() ||
89  !uNodeIndex.parseHex(
90  context.params[jss::deposit_preauth].asString()))
91  {
92  uNodeIndex = beast::zero;
93  jvResult[jss::error] = "malformedRequest";
94  }
95  }
96  else if (
97  !context.params[jss::deposit_preauth].isMember(jss::owner) ||
98  !context.params[jss::deposit_preauth][jss::owner].isString() ||
99  !context.params[jss::deposit_preauth].isMember(
100  jss::authorized) ||
101  !context.params[jss::deposit_preauth][jss::authorized]
102  .isString())
103  {
104  jvResult[jss::error] = "malformedRequest";
105  }
106  else
107  {
108  auto const owner = parseBase58<AccountID>(
109  context.params[jss::deposit_preauth][jss::owner]
110  .asString());
111 
112  auto const authorized = parseBase58<AccountID>(
113  context.params[jss::deposit_preauth][jss::authorized]
114  .asString());
115 
116  if (!owner)
117  jvResult[jss::error] = "malformedOwner";
118  else if (!authorized)
119  jvResult[jss::error] = "malformedAuthorized";
120  else
121  uNodeIndex =
123  }
124  }
125  else if (context.params.isMember(jss::directory))
126  {
127  expectedType = ltDIR_NODE;
128  if (context.params[jss::directory].isNull())
129  {
130  jvResult[jss::error] = "malformedRequest";
131  }
132  else if (!context.params[jss::directory].isObject())
133  {
134  if (!uNodeIndex.parseHex(
135  context.params[jss::directory].asString()))
136  {
137  uNodeIndex = beast::zero;
138  jvResult[jss::error] = "malformedRequest";
139  }
140  }
141  else if (
142  context.params[jss::directory].isMember(jss::sub_index) &&
143  !context.params[jss::directory][jss::sub_index].isIntegral())
144  {
145  jvResult[jss::error] = "malformedRequest";
146  }
147  else
148  {
149  std::uint64_t uSubIndex =
150  context.params[jss::directory].isMember(jss::sub_index)
151  ? context.params[jss::directory][jss::sub_index].asUInt()
152  : 0;
153 
154  if (context.params[jss::directory].isMember(jss::dir_root))
155  {
156  uint256 uDirRoot;
157 
158  if (context.params[jss::directory].isMember(jss::owner))
159  {
160  // May not specify both dir_root and owner.
161  jvResult[jss::error] = "malformedRequest";
162  }
163  else if (!uDirRoot.parseHex(
164  context.params[jss::directory][jss::dir_root]
165  .asString()))
166  {
167  uNodeIndex = beast::zero;
168  jvResult[jss::error] = "malformedRequest";
169  }
170  else
171  {
172  uNodeIndex = keylet::page(uDirRoot, uSubIndex).key;
173  }
174  }
175  else if (context.params[jss::directory].isMember(jss::owner))
176  {
177  auto const ownerID = parseBase58<AccountID>(
178  context.params[jss::directory][jss::owner].asString());
179 
180  if (!ownerID)
181  {
182  jvResult[jss::error] = "malformedAddress";
183  }
184  else
185  {
186  uNodeIndex =
187  keylet::page(keylet::ownerDir(*ownerID), uSubIndex)
188  .key;
189  }
190  }
191  else
192  {
193  jvResult[jss::error] = "malformedRequest";
194  }
195  }
196  }
197  else if (context.params.isMember(jss::escrow))
198  {
199  expectedType = ltESCROW;
200  if (!context.params[jss::escrow].isObject())
201  {
202  if (!uNodeIndex.parseHex(
203  context.params[jss::escrow].asString()))
204  {
205  uNodeIndex = beast::zero;
206  jvResult[jss::error] = "malformedRequest";
207  }
208  }
209  else if (
210  !context.params[jss::escrow].isMember(jss::owner) ||
211  !context.params[jss::escrow].isMember(jss::seq) ||
212  !context.params[jss::escrow][jss::seq].isIntegral())
213  {
214  jvResult[jss::error] = "malformedRequest";
215  }
216  else
217  {
218  auto const id = parseBase58<AccountID>(
219  context.params[jss::escrow][jss::owner].asString());
220  if (!id)
221  jvResult[jss::error] = "malformedOwner";
222  else
223  uNodeIndex =
225  *id, context.params[jss::escrow][jss::seq].asUInt())
226  .key;
227  }
228  }
229  else if (context.params.isMember(jss::offer))
230  {
231  expectedType = ltOFFER;
232  if (!context.params[jss::offer].isObject())
233  {
234  if (!uNodeIndex.parseHex(context.params[jss::offer].asString()))
235  {
236  uNodeIndex = beast::zero;
237  jvResult[jss::error] = "malformedRequest";
238  }
239  }
240  else if (
241  !context.params[jss::offer].isMember(jss::account) ||
242  !context.params[jss::offer].isMember(jss::seq) ||
243  !context.params[jss::offer][jss::seq].isIntegral())
244  {
245  jvResult[jss::error] = "malformedRequest";
246  }
247  else
248  {
249  auto const id = parseBase58<AccountID>(
250  context.params[jss::offer][jss::account].asString());
251  if (!id)
252  jvResult[jss::error] = "malformedAddress";
253  else
254  uNodeIndex =
256  *id, context.params[jss::offer][jss::seq].asUInt())
257  .key;
258  }
259  }
260  else if (context.params.isMember(jss::payment_channel))
261  {
262  expectedType = ltPAYCHAN;
263 
264  if (!uNodeIndex.parseHex(
265  context.params[jss::payment_channel].asString()))
266  {
267  uNodeIndex = beast::zero;
268  jvResult[jss::error] = "malformedRequest";
269  }
270  }
271  else if (context.params.isMember(jss::ripple_state))
272  {
273  expectedType = ltRIPPLE_STATE;
274  Currency uCurrency;
275  Json::Value jvRippleState = context.params[jss::ripple_state];
276 
277  if (!jvRippleState.isObject() ||
278  !jvRippleState.isMember(jss::currency) ||
279  !jvRippleState.isMember(jss::accounts) ||
280  !jvRippleState[jss::accounts].isArray() ||
281  2 != jvRippleState[jss::accounts].size() ||
282  !jvRippleState[jss::accounts][0u].isString() ||
283  !jvRippleState[jss::accounts][1u].isString() ||
284  (jvRippleState[jss::accounts][0u].asString() ==
285  jvRippleState[jss::accounts][1u].asString()))
286  {
287  jvResult[jss::error] = "malformedRequest";
288  }
289  else
290  {
291  auto const id1 = parseBase58<AccountID>(
292  jvRippleState[jss::accounts][0u].asString());
293  auto const id2 = parseBase58<AccountID>(
294  jvRippleState[jss::accounts][1u].asString());
295  if (!id1 || !id2)
296  {
297  jvResult[jss::error] = "malformedAddress";
298  }
299  else if (!to_currency(
300  uCurrency,
301  jvRippleState[jss::currency].asString()))
302  {
303  jvResult[jss::error] = "malformedCurrency";
304  }
305  else
306  {
307  uNodeIndex = keylet::line(*id1, *id2, uCurrency).key;
308  }
309  }
310  }
311  else if (context.params.isMember(jss::ticket))
312  {
313  expectedType = ltTICKET;
314  if (!context.params[jss::ticket].isObject())
315  {
316  if (!uNodeIndex.parseHex(
317  context.params[jss::ticket].asString()))
318  {
319  uNodeIndex = beast::zero;
320  jvResult[jss::error] = "malformedRequest";
321  }
322  }
323  else if (
324  !context.params[jss::ticket].isMember(jss::account) ||
325  !context.params[jss::ticket].isMember(jss::ticket_seq) ||
326  !context.params[jss::ticket][jss::ticket_seq].isIntegral())
327  {
328  jvResult[jss::error] = "malformedRequest";
329  }
330  else
331  {
332  auto const id = parseBase58<AccountID>(
333  context.params[jss::ticket][jss::account].asString());
334  if (!id)
335  jvResult[jss::error] = "malformedAddress";
336  else
337  uNodeIndex = getTicketIndex(
338  *id,
339  context.params[jss::ticket][jss::ticket_seq].asUInt());
340  }
341  }
342  else if (context.params.isMember(jss::nft_page))
343  {
344  expectedType = ltNFTOKEN_PAGE;
345 
346  if (context.params[jss::nft_page].isString())
347  {
348  if (!uNodeIndex.parseHex(
349  context.params[jss::nft_page].asString()))
350  {
351  uNodeIndex = beast::zero;
352  jvResult[jss::error] = "malformedRequest";
353  }
354  }
355  else
356  {
357  jvResult[jss::error] = "malformedRequest";
358  }
359  }
360  else if (context.params.isMember(jss::amm))
361  {
362  expectedType = ltAMM;
363  if (!context.params[jss::amm].isObject())
364  {
365  if (!uNodeIndex.parseHex(context.params[jss::amm].asString()))
366  {
367  uNodeIndex = beast::zero;
368  jvResult[jss::error] = "malformedRequest";
369  }
370  }
371  else if (
372  !context.params[jss::amm].isMember(jss::asset) ||
373  !context.params[jss::amm].isMember(jss::asset2))
374  {
375  jvResult[jss::error] = "malformedRequest";
376  }
377  else
378  {
379  try
380  {
381  auto const issue =
382  issueFromJson(context.params[jss::amm][jss::asset]);
383  auto const issue2 =
384  issueFromJson(context.params[jss::amm][jss::asset2]);
385  uNodeIndex = keylet::amm(issue, issue2).key;
386  }
387  catch (std::runtime_error const&)
388  {
389  jvResult[jss::error] = "malformedRequest";
390  }
391  }
392  }
393  else
394  {
395  if (context.params.isMember("params") &&
396  context.params["params"].isArray() &&
397  context.params["params"].size() == 1 &&
398  context.params["params"][0u].isString())
399  {
400  if (!uNodeIndex.parseHex(
401  context.params["params"][0u].asString()))
402  {
403  uNodeIndex = beast::zero;
404  jvResult[jss::error] = "malformedRequest";
405  }
406  }
407  else
408  {
409  if (context.apiVersion < 2u)
410  jvResult[jss::error] = "unknownOption";
411  else
412  jvResult[jss::error] = "invalidParams";
413  }
414  }
415  }
416  catch (Json::error& e)
417  {
418  if (context.apiVersion > 1u)
419  {
420  // For apiVersion 2 onwards, any parsing failures that throw
421  // this
422  // exception return an invalidParam error.
423  uNodeIndex = beast::zero;
424  jvResult[jss::error] = "invalidParams";
425  }
426  else
427  throw;
428  }
429 
430  if (uNodeIndex.isNonZero())
431  {
432  auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
433  if (context.params.isMember(jss::binary))
434  bNodeBinary = context.params[jss::binary].asBool();
435 
436  if (!sleNode)
437  {
438  // Not found.
439  jvResult[jss::error] = "entryNotFound";
440  }
441  else if (
442  (expectedType != ltANY) && (expectedType != sleNode->getType()))
443  {
444  jvResult[jss::error] = "unexpectedLedgerType";
445  }
446  else if (bNodeBinary)
447  {
448  Serializer s;
449 
450  sleNode->add(s);
451 
452  jvResult[jss::node_binary] = strHex(s.peekData());
453  jvResult[jss::index] = to_string(uNodeIndex);
454  }
455  else
456  {
457  jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
458  jvResult[jss::index] = to_string(uNodeIndex);
459  }
460  }
461 
462  return jvResult;
463 }
464 
468 {
469  org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
470  org::xrpl::rpc::v1::GetLedgerEntryResponse response;
471  grpc::Status status = grpc::Status::OK;
472 
474  if (auto status = RPC::ledgerFromRequest(ledger, context))
475  {
476  grpc::Status errorStatus;
477  if (status.toErrorCode() == rpcINVALID_PARAMS)
478  {
479  errorStatus = grpc::Status(
480  grpc::StatusCode::INVALID_ARGUMENT, status.message());
481  }
482  else
483  {
484  errorStatus =
485  grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
486  }
487  return {response, errorStatus};
488  }
489 
490  auto key = uint256::fromVoidChecked(request.key());
491  if (!key)
492  {
493  grpc::Status errorStatus{
494  grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
495  return {response, errorStatus};
496  }
497 
498  auto const sleNode = ledger->read(keylet::unchecked(*key));
499  if (!sleNode)
500  {
501  grpc::Status errorStatus{
502  grpc::StatusCode::NOT_FOUND, "object not found"};
503  return {response, errorStatus};
504  }
505  else
506  {
507  Serializer s;
508  sleNode->add(s);
509 
510  auto& stateObject = *response.mutable_ledger_object();
511  stateObject.set_data(s.peekData().data(), s.getLength());
512  stateObject.set_key(request.key());
513  *(response.mutable_ledger()) = request.ledger();
514  return {response, status};
515  }
516 }
517 } // namespace ripple
Json::error
Definition: json_errors.h:27
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:304
ripple::to_currency
bool to_currency(Currency &currency, std::string const &code)
Tries to convert a string to a Currency, returns true on success.
Definition: UintTypes.cpp:80
ripple::RPC::JsonContext
Definition: Context.h:53
ripple::ltTICKET
@ ltTICKET
A ledger object which describes a ticket.
Definition: LedgerFormats.h:80
ripple::doLedgerEntry
Json::Value doLedgerEntry(RPC::JsonContext &)
Definition: LedgerEntry.cpp:41
Json::Value::isObject
bool isObject() const
Definition: json_value.cpp:1027
std::shared_ptr
STL class.
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
Json::Value::isString
bool isString() const
Definition: json_value.cpp:1009
ripple::ltANY
@ ltANY
A special type, matching any ledger entry type.
Definition: LedgerFormats.h:182
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:537
std::pair
ripple::keylet::amm
Keylet amm(Issue const &issue1, Issue const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:375
ripple::keylet::offer
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:223
Json::Value::isNull
bool isNull() const
isNull() tests to see if this field is null.
Definition: json_value.cpp:967
ripple::ltCHECK
@ ltCHECK
A ledger object which describes a check.
Definition: LedgerFormats.h:136
ripple::base_uint< 256 >::fromVoidChecked
static std::optional< base_uint > fromVoidChecked(T const &from)
Definition: base_uint.h:319
ripple::RPC::lookupLedger
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:676
ripple::getTicketIndex
uint256 getTicketIndex(AccountID const &account, std::uint32_t ticketSeq)
Definition: Indexes.cpp:116
ripple::ltDIR_NODE
@ ltDIR_NODE
A ledger object which contains a list of object identifiers.
Definition: LedgerFormats.h:66
Json::Value::asBool
bool asBool() const
Definition: json_value.cpp:619
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint< 256 >
ripple::keylet::escrow
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition: Indexes.cpp:319
ripple::ltOFFER
@ ltOFFER
A ledger object which describes an offer on the DEX.
Definition: LedgerFormats.h:92
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:532
ripple::authorized
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
Definition: ServerHandler.cpp:85
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:134
ripple::ltESCROW
@ ltESCROW
A ledger object describing a single escrow.
Definition: LedgerFormats.h:124
ripple::doLedgerEntryGrpc
std::pair< org::xrpl::rpc::v1::GetLedgerEntryResponse, grpc::Status > doLedgerEntryGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerEntryRequest > &context)
Definition: LedgerEntry.cpp:466
ripple::JsonOptions::none
@ none
ripple::keylet::page
Keylet page(uint256 const &key, std::uint64_t index) noexcept
A page in a directory.
Definition: Indexes.cpp:310
ripple::RPC::GRPCContext
Definition: Context.h:70
ripple::ltDEPOSIT_PREAUTH
@ ltDEPOSIT_PREAUTH
A ledger object which describes a deposit preauthorization.
Definition: LedgerFormats.h:142
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
std::runtime_error
STL class.
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
std::uint64_t
ripple::issueFromJson
Issue issueFromJson(Json::Value const &v)
Definition: Issue.cpp:78
ripple::keylet::line
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:194
ripple::keylet::unchecked
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition: Indexes.cpp:298
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
Json::Value::isArray
bool isArray() const
Definition: json_value.cpp:1015
ripple::Serializer
Definition: Serializer.h:40
ripple::RPC::GRPCContext::params
RequestType params
Definition: Context.h:72
ripple::ltNFTOKEN_PAGE
@ ltNFTOKEN_PAGE
A ledger object which contains a list of NFTs.
Definition: LedgerFormats.h:156
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::LedgerEntryType
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:53
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
A ledger object which describes an account.
Definition: LedgerFormats.h:59
ripple::Serializer::peekData
Blob const & peekData() const
Definition: Serializer.h:169
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
Json::Value::isIntegral
bool isIntegral() const
Definition: json_value.cpp:991
ripple::RPC::Context::apiVersion
unsigned int apiVersion
Definition: Context.h:50
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::ltRIPPLE_STATE
@ ltRIPPLE_STATE
A ledger object which describes a bidirectional trust line.
Definition: LedgerFormats.h:74
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
ripple::base_uint::parseHex
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:496
ripple::Serializer::getLength
int getLength() const
Definition: Serializer.h:200
ripple::keylet::depositPreauth
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:288
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
std::vector::data
T data(T... args)
ripple::RPC::ledgerFromRequest
Status ledgerFromRequest(T &ledger, GRPCContext< R > &context)
Definition: RPCHelpers.cpp:395
ripple::ltAMM
@ ltAMM
The ledger object which tracks the AMM.
Definition: LedgerFormats.h:168
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::ltPAYCHAN
@ ltPAYCHAN
A ledger object describing a single unidirectional XRP payment channel.
Definition: LedgerFormats.h:130
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469