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
348  {
349  if (context.params.isMember("params") &&
350  context.params["params"].isArray() &&
351  context.params["params"].size() == 1 &&
352  context.params["params"][0u].isString())
353  {
354  if (!uNodeIndex.parseHex(context.params["params"][0u].asString()))
355  {
356  uNodeIndex = beast::zero;
357  jvResult[jss::error] = "malformedRequest";
358  }
359  }
360  else
361  jvResult[jss::error] = "unknownOption";
362  }
363 
364  if (uNodeIndex.isNonZero())
365  {
366  auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
367  if (context.params.isMember(jss::binary))
368  bNodeBinary = context.params[jss::binary].asBool();
369 
370  if (!sleNode)
371  {
372  // Not found.
373  jvResult[jss::error] = "entryNotFound";
374  }
375  else if (
376  (expectedType != ltANY) && (expectedType != sleNode->getType()))
377  {
378  jvResult[jss::error] = "unexpectedLedgerType";
379  }
380  else if (bNodeBinary)
381  {
382  Serializer s;
383 
384  sleNode->add(s);
385 
386  jvResult[jss::node_binary] = strHex(s.peekData());
387  jvResult[jss::index] = to_string(uNodeIndex);
388  }
389  else
390  {
391  jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
392  jvResult[jss::index] = to_string(uNodeIndex);
393  }
394  }
395 
396  return jvResult;
397 }
398 
402 {
403  org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
404  org::xrpl::rpc::v1::GetLedgerEntryResponse response;
405  grpc::Status status = grpc::Status::OK;
406 
408  if (auto status = RPC::ledgerFromRequest(ledger, context))
409  {
410  grpc::Status errorStatus;
411  if (status.toErrorCode() == rpcINVALID_PARAMS)
412  {
413  errorStatus = grpc::Status(
414  grpc::StatusCode::INVALID_ARGUMENT, status.message());
415  }
416  else
417  {
418  errorStatus =
419  grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
420  }
421  return {response, errorStatus};
422  }
423 
424  auto key = uint256::fromVoidChecked(request.key());
425  if (!key)
426  {
427  grpc::Status errorStatus{
428  grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
429  return {response, errorStatus};
430  }
431 
432  auto const sleNode = ledger->read(keylet::unchecked(*key));
433  if (!sleNode)
434  {
435  grpc::Status errorStatus{
436  grpc::StatusCode::NOT_FOUND, "object not found"};
437  return {response, errorStatus};
438  }
439  else
440  {
441  Serializer s;
442  sleNode->add(s);
443 
444  auto& stateObject = *response.mutable_ledger_object();
445  stateObject.set_data(s.peekData().data(), s.getLength());
446  stateObject.set_key(request.key());
447  *(response.mutable_ledger()) = request.ledger();
448  return {response, status};
449  }
450 }
451 } // namespace ripple
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:303
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:176
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:537
std::pair
ripple::keylet::offer
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:222
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:675
ripple::getTicketIndex
uint256 getTicketIndex(AccountID const &account, std::uint32_t ticketSeq)
Definition: Indexes.cpp:115
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:318
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: ServerHandlerImp.cpp:85
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
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:400
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:309
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
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::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:193
ripple::keylet::unchecked
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition: Indexes.cpp:297
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:39
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:168
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
Json::Value::isIntegral
bool isIntegral() const
Definition: json_value.cpp:991
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:199
ripple::keylet::depositPreauth
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:287
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:394
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