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
332  {
333  jvResult[jss::error] = "unknownOption";
334  }
335 
336  if (uNodeIndex.isNonZero())
337  {
338  auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
339  if (context.params.isMember(jss::binary))
340  bNodeBinary = context.params[jss::binary].asBool();
341 
342  if (!sleNode)
343  {
344  // Not found.
345  jvResult[jss::error] = "entryNotFound";
346  }
347  else if (
348  (expectedType != ltANY) && (expectedType != sleNode->getType()))
349  {
350  jvResult[jss::error] = "malformedRequest";
351  }
352  else if (bNodeBinary)
353  {
354  Serializer s;
355 
356  sleNode->add(s);
357 
358  jvResult[jss::node_binary] = strHex(s.peekData());
359  jvResult[jss::index] = to_string(uNodeIndex);
360  }
361  else
362  {
363  jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
364  jvResult[jss::index] = to_string(uNodeIndex);
365  }
366  }
367 
368  return jvResult;
369 }
370 
374 {
375  org::xrpl::rpc::v1::GetLedgerEntryRequest& request = context.params;
376  org::xrpl::rpc::v1::GetLedgerEntryResponse response;
377  grpc::Status status = grpc::Status::OK;
378 
380  if (auto status = RPC::ledgerFromRequest(ledger, context))
381  {
382  grpc::Status errorStatus;
383  if (status.toErrorCode() == rpcINVALID_PARAMS)
384  {
385  errorStatus = grpc::Status(
386  grpc::StatusCode::INVALID_ARGUMENT, status.message());
387  }
388  else
389  {
390  errorStatus =
391  grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
392  }
393  return {response, errorStatus};
394  }
395 
396  auto key = uint256::fromVoidChecked(request.key());
397  if (!key)
398  {
399  grpc::Status errorStatus{
400  grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
401  return {response, errorStatus};
402  }
403 
404  auto const sleNode = ledger->read(keylet::unchecked(*key));
405  if (!sleNode)
406  {
407  grpc::Status errorStatus{
408  grpc::StatusCode::NOT_FOUND, "object not found"};
409  return {response, errorStatus};
410  }
411  else
412  {
413  Serializer s;
414  sleNode->add(s);
415 
416  auto& stateObject = *response.mutable_ledger_object();
417  stateObject.set_data(s.peekData().data(), s.getLength());
418  stateObject.set_key(request.key());
419  *(response.mutable_ledger()) = request.ledger();
420  return {response, status};
421  }
422 }
423 } // namespace ripple
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:300
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:164
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:516
std::pair
ripple::keylet::offer
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:219
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:540
ripple::getTicketIndex
uint256 getTicketIndex(AccountID const &account, std::uint32_t ticketSeq)
Definition: Indexes.cpp:112
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:315
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:511
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:130
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:372
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:306
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:190
ripple::keylet::unchecked
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition: Indexes.cpp:294
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
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:38
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:475
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:284
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:253
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