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/ledger/ReadView.h>
24 #include <ripple/net/RPCErr.h>
25 #include <ripple/protocol/ErrorCodes.h>
26 #include <ripple/protocol/Indexes.h>
27 #include <ripple/protocol/jss.h>
28 #include <ripple/rpc/Context.h>
29 #include <ripple/rpc/GRPCHandlers.h>
30 #include <ripple/rpc/impl/RPCHelpers.h>
31 
32 namespace ripple {
33 
34 // {
35 // ledger_hash : <ledger>
36 // ledger_index : <ledger_index>
37 // ...
38 // }
41 {
43  auto jvResult = RPC::lookupLedger(lpLedger, context);
44 
45  if (!lpLedger)
46  return jvResult;
47 
48  uint256 uNodeIndex;
49  bool bNodeBinary = false;
50  LedgerEntryType expectedType = ltANY;
51 
52  if (context.params.isMember(jss::index))
53  {
54  if (!uNodeIndex.parseHex(context.params[jss::index].asString()))
55  {
56  uNodeIndex = beast::zero;
57  jvResult[jss::error] = "malformedRequest";
58  }
59  }
60  else if (context.params.isMember(jss::account_root))
61  {
62  expectedType = ltACCOUNT_ROOT;
63  auto const account = parseBase58<AccountID>(
64  context.params[jss::account_root].asString());
65  if (!account || account->isZero())
66  jvResult[jss::error] = "malformedAddress";
67  else
68  uNodeIndex = keylet::account(*account).key;
69  }
70  else if (context.params.isMember(jss::check))
71  {
72  expectedType = ltCHECK;
73 
74  if (!uNodeIndex.parseHex(context.params[jss::check].asString()))
75  {
76  uNodeIndex = beast::zero;
77  jvResult[jss::error] = "malformedRequest";
78  }
79  }
80  else if (context.params.isMember(jss::deposit_preauth))
81  {
82  expectedType = ltDEPOSIT_PREAUTH;
83 
84  if (!context.params[jss::deposit_preauth].isObject())
85  {
86  if (!context.params[jss::deposit_preauth].isString() ||
87  !uNodeIndex.parseHex(
88  context.params[jss::deposit_preauth].asString()))
89  {
90  uNodeIndex = beast::zero;
91  jvResult[jss::error] = "malformedRequest";
92  }
93  }
94  else if (
95  !context.params[jss::deposit_preauth].isMember(jss::owner) ||
96  !context.params[jss::deposit_preauth][jss::owner].isString() ||
97  !context.params[jss::deposit_preauth].isMember(jss::authorized) ||
98  !context.params[jss::deposit_preauth][jss::authorized].isString())
99  {
100  jvResult[jss::error] = "malformedRequest";
101  }
102  else
103  {
104  auto const owner = parseBase58<AccountID>(
105  context.params[jss::deposit_preauth][jss::owner].asString());
106 
107  auto const authorized = parseBase58<AccountID>(
108  context.params[jss::deposit_preauth][jss::authorized]
109  .asString());
110 
111  if (!owner)
112  jvResult[jss::error] = "malformedOwner";
113  else if (!authorized)
114  jvResult[jss::error] = "malformedAuthorized";
115  else
116  uNodeIndex = keylet::depositPreauth(*owner, *authorized).key;
117  }
118  }
119  else if (context.params.isMember(jss::directory))
120  {
121  expectedType = ltDIR_NODE;
122  if (context.params[jss::directory].isNull())
123  {
124  jvResult[jss::error] = "malformedRequest";
125  }
126  else if (!context.params[jss::directory].isObject())
127  {
128  if (!uNodeIndex.parseHex(context.params[jss::directory].asString()))
129  {
130  uNodeIndex = beast::zero;
131  jvResult[jss::error] = "malformedRequest";
132  }
133  }
134  else if (
135  context.params[jss::directory].isMember(jss::sub_index) &&
136  !context.params[jss::directory][jss::sub_index].isIntegral())
137  {
138  jvResult[jss::error] = "malformedRequest";
139  }
140  else
141  {
142  std::uint64_t uSubIndex =
143  context.params[jss::directory].isMember(jss::sub_index)
144  ? context.params[jss::directory][jss::sub_index].asUInt()
145  : 0;
146 
147  if (context.params[jss::directory].isMember(jss::dir_root))
148  {
149  uint256 uDirRoot;
150 
151  if (context.params[jss::directory].isMember(jss::owner))
152  {
153  // May not specify both dir_root and owner.
154  jvResult[jss::error] = "malformedRequest";
155  }
156  else if (!uDirRoot.parseHex(
157  context.params[jss::directory][jss::dir_root]
158  .asString()))
159  {
160  uNodeIndex = beast::zero;
161  jvResult[jss::error] = "malformedRequest";
162  }
163  else
164  {
165  uNodeIndex = keylet::page(uDirRoot, uSubIndex).key;
166  }
167  }
168  else if (context.params[jss::directory].isMember(jss::owner))
169  {
170  auto const ownerID = parseBase58<AccountID>(
171  context.params[jss::directory][jss::owner].asString());
172 
173  if (!ownerID)
174  {
175  jvResult[jss::error] = "malformedAddress";
176  }
177  else
178  {
179  uNodeIndex =
180  keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
181  }
182  }
183  else
184  {
185  jvResult[jss::error] = "malformedRequest";
186  }
187  }
188  }
189  else if (context.params.isMember(jss::escrow))
190  {
191  expectedType = ltESCROW;
192  if (!context.params[jss::escrow].isObject())
193  {
194  if (!uNodeIndex.parseHex(context.params[jss::escrow].asString()))
195  {
196  uNodeIndex = beast::zero;
197  jvResult[jss::error] = "malformedRequest";
198  }
199  }
200  else if (
201  !context.params[jss::escrow].isMember(jss::owner) ||
202  !context.params[jss::escrow].isMember(jss::seq) ||
203  !context.params[jss::escrow][jss::seq].isIntegral())
204  {
205  jvResult[jss::error] = "malformedRequest";
206  }
207  else
208  {
209  auto const id = parseBase58<AccountID>(
210  context.params[jss::escrow][jss::owner].asString());
211  if (!id)
212  jvResult[jss::error] = "malformedOwner";
213  else
214  uNodeIndex =
216  *id, context.params[jss::escrow][jss::seq].asUInt())
217  .key;
218  }
219  }
220  else if (context.params.isMember(jss::offer))
221  {
222  expectedType = ltOFFER;
223  if (!context.params[jss::offer].isObject())
224  {
225  if (!uNodeIndex.parseHex(context.params[jss::offer].asString()))
226  {
227  uNodeIndex = beast::zero;
228  jvResult[jss::error] = "malformedRequest";
229  }
230  }
231  else if (
232  !context.params[jss::offer].isMember(jss::account) ||
233  !context.params[jss::offer].isMember(jss::seq) ||
234  !context.params[jss::offer][jss::seq].isIntegral())
235  {
236  jvResult[jss::error] = "malformedRequest";
237  }
238  else
239  {
240  auto const id = parseBase58<AccountID>(
241  context.params[jss::offer][jss::account].asString());
242  if (!id)
243  jvResult[jss::error] = "malformedAddress";
244  else
245  uNodeIndex =
247  *id, context.params[jss::offer][jss::seq].asUInt())
248  .key;
249  }
250  }
251  else if (context.params.isMember(jss::payment_channel))
252  {
253  expectedType = ltPAYCHAN;
254 
255  if (!uNodeIndex.parseHex(
256  context.params[jss::payment_channel].asString()))
257  {
258  uNodeIndex = beast::zero;
259  jvResult[jss::error] = "malformedRequest";
260  }
261  }
262  else if (context.params.isMember(jss::ripple_state))
263  {
264  expectedType = ltRIPPLE_STATE;
265  Currency uCurrency;
266  Json::Value jvRippleState = context.params[jss::ripple_state];
267 
268  if (!jvRippleState.isObject() ||
269  !jvRippleState.isMember(jss::currency) ||
270  !jvRippleState.isMember(jss::accounts) ||
271  !jvRippleState[jss::accounts].isArray() ||
272  2 != jvRippleState[jss::accounts].size() ||
273  !jvRippleState[jss::accounts][0u].isString() ||
274  !jvRippleState[jss::accounts][1u].isString() ||
275  (jvRippleState[jss::accounts][0u].asString() ==
276  jvRippleState[jss::accounts][1u].asString()))
277  {
278  jvResult[jss::error] = "malformedRequest";
279  }
280  else
281  {
282  auto const id1 = parseBase58<AccountID>(
283  jvRippleState[jss::accounts][0u].asString());
284  auto const id2 = parseBase58<AccountID>(
285  jvRippleState[jss::accounts][1u].asString());
286  if (!id1 || !id2)
287  {
288  jvResult[jss::error] = "malformedAddress";
289  }
290  else if (!to_currency(
291  uCurrency, jvRippleState[jss::currency].asString()))
292  {
293  jvResult[jss::error] = "malformedCurrency";
294  }
295  else
296  {
297  uNodeIndex = keylet::line(*id1, *id2, uCurrency).key;
298  }
299  }
300  }
301  else if (context.params.isMember(jss::ticket))
302  {
303  expectedType = ltTICKET;
304  if (!context.params[jss::ticket].isObject())
305  {
306  if (!uNodeIndex.parseHex(context.params[jss::ticket].asString()))
307  {
308  uNodeIndex = beast::zero;
309  jvResult[jss::error] = "malformedRequest";
310  }
311  }
312  else if (
313  !context.params[jss::ticket].isMember(jss::account) ||
314  !context.params[jss::ticket].isMember(jss::ticket_seq) ||
315  !context.params[jss::ticket][jss::ticket_seq].isIntegral())
316  {
317  jvResult[jss::error] = "malformedRequest";
318  }
319  else
320  {
321  auto const id = parseBase58<AccountID>(
322  context.params[jss::ticket][jss::account].asString());
323  if (!id)
324  jvResult[jss::error] = "malformedAddress";
325  else
326  uNodeIndex = getTicketIndex(
327  *id, context.params[jss::ticket][jss::ticket_seq].asUInt());
328  }
329  }
330  else if (context.params.isMember(jss::nft_page))
331  {
332  expectedType = ltNFTOKEN_PAGE;
333 
334  if (context.params[jss::nft_page].isString())
335  {
336  if (!uNodeIndex.parseHex(context.params[jss::nft_page].asString()))
337  {
338  uNodeIndex = beast::zero;
339  jvResult[jss::error] = "malformedRequest";
340  }
341  }
342  else
343  {
344  jvResult[jss::error] = "malformedRequest";
345  }
346  }
347  else if (context.params.isMember(jss::amm))
348  {
349  expectedType = ltAMM;
350  if (!context.params[jss::amm].isObject())
351  {
352  if (!uNodeIndex.parseHex(context.params[jss::amm].asString()))
353  {
354  uNodeIndex = beast::zero;
355  jvResult[jss::error] = "malformedRequest";
356  }
357  }
358  else if (
359  !context.params[jss::amm].isMember(jss::asset) ||
360  !context.params[jss::amm].isMember(jss::asset2))
361  {
362  jvResult[jss::error] = "malformedRequest";
363  }
364  else
365  {
366  try
367  {
368  auto const issue =
369  issueFromJson(context.params[jss::amm][jss::asset]);
370  auto const issue2 =
371  issueFromJson(context.params[jss::amm][jss::asset2]);
372  uNodeIndex = keylet::amm(issue, issue2).key;
373  }
374  catch (std::runtime_error const&)
375  {
376  jvResult[jss::error] = "malformedRequest";
377  }
378  }
379  }
380  else
381  {
382  if (context.params.isMember("params") &&
383  context.params["params"].isArray() &&
384  context.params["params"].size() == 1 &&
385  context.params["params"][0u].isString())
386  {
387  if (!uNodeIndex.parseHex(context.params["params"][0u].asString()))
388  {
389  uNodeIndex = beast::zero;
390  jvResult[jss::error] = "malformedRequest";
391  }
392  }
393  else
394  {
395  if (context.apiVersion < 2u)
396  jvResult[jss::error] = "unknownOption";
397  else
398  jvResult[jss::error] = "invalidParams";
399  }
400  }
401 
402  if (uNodeIndex.isNonZero())
403  {
404  auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
405  if (context.params.isMember(jss::binary))
406  bNodeBinary = context.params[jss::binary].asBool();
407 
408  if (!sleNode)
409  {
410  // Not found.
411  jvResult[jss::error] = "entryNotFound";
412  }
413  else if (
414  (expectedType != ltANY) && (expectedType != sleNode->getType()))
415  {
416  jvResult[jss::error] = "unexpectedLedgerType";
417  }
418  else if (bNodeBinary)
419  {
420  Serializer s;
421 
422  sleNode->add(s);
423 
424  jvResult[jss::node_binary] = strHex(s.peekData());
425  jvResult[jss::index] = to_string(uNodeIndex);
426  }
427  else
428  {
429  jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
430  jvResult[jss::index] = to_string(uNodeIndex);
431  }
432  }
433 
434  return jvResult;
435 }
436 
440 {
441  org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
442  org::xrpl::rpc::v1::GetLedgerEntryResponse response;
443  grpc::Status status = grpc::Status::OK;
444 
446  if (auto status = RPC::ledgerFromRequest(ledger, context))
447  {
448  grpc::Status errorStatus;
449  if (status.toErrorCode() == rpcINVALID_PARAMS)
450  {
451  errorStatus = grpc::Status(
452  grpc::StatusCode::INVALID_ARGUMENT, status.message());
453  }
454  else
455  {
456  errorStatus =
457  grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
458  }
459  return {response, errorStatus};
460  }
461 
462  auto key = uint256::fromVoidChecked(request.key());
463  if (!key)
464  {
465  grpc::Status errorStatus{
466  grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
467  return {response, errorStatus};
468  }
469 
470  auto const sleNode = ledger->read(keylet::unchecked(*key));
471  if (!sleNode)
472  {
473  grpc::Status errorStatus{
474  grpc::StatusCode::NOT_FOUND, "object not found"};
475  return {response, errorStatus};
476  }
477  else
478  {
479  Serializer s;
480  sleNode->add(s);
481 
482  auto& stateObject = *response.mutable_ledger_object();
483  stateObject.set_data(s.peekData().data(), s.getLength());
484  stateObject.set_key(request.key());
485  *(response.mutable_ledger()) = request.ledger();
486  return {response, status};
487  }
488 }
489 } // namespace ripple
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:40
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:438
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:77
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