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