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 (RPC::ledgerFromRequest(ledger, context))
381  {
382  grpc::Status errorStatus{
383  grpc::StatusCode::NOT_FOUND, "ledger not found"};
384  return {response, errorStatus};
385  }
386 
387  std::string const& keyBytes = request.key();
388  auto key = uint256::fromVoid(keyBytes.data());
389  if (keyBytes.size() != key.size())
390  {
391  grpc::Status errorStatus{
392  grpc::StatusCode::INVALID_ARGUMENT, "index malformed"};
393  return {response, errorStatus};
394  }
395 
396  auto const sleNode = ledger->read(keylet::unchecked(key));
397  if (!sleNode)
398  {
399  grpc::Status errorStatus{
400  grpc::StatusCode::NOT_FOUND, "object not found"};
401  return {response, errorStatus};
402  }
403  else
404  {
405  Serializer s;
406  sleNode->add(s);
407 
408  auto& stateObject = *response.mutable_ledger_object();
409  stateObject.set_data(s.peekData().data(), s.getLength());
410  stateObject.set_key(request.key());
411  *(response.mutable_ledger()) = request.ledger();
412  return {response, status};
413  }
414 }
415 } // 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:83
ripple::RPC::JsonContext
Definition: Context.h:53
ripple::doLedgerEntry
Json::Value doLedgerEntry(RPC::JsonContext &)
Definition: LedgerEntry.cpp:41
Json::Value::isObject
bool isObject() const
Definition: json_value.cpp:1027
std::string
STL class.
std::shared_ptr
STL class.
Json::Value::isString
bool isString() const
Definition: json_value.cpp:1009
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:444
std::pair
ripple::ltESCROW
@ ltESCROW
Definition: LedgerFormats.h:80
std::string::size
T size(T... args)
ripple::ltANY
@ ltANY
Special type, anything This is used when the type in the Keylet is unknown, such as when building met...
Definition: LedgerFormats.h:41
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::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:45
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:535
ripple::getTicketIndex
uint256 getTicketIndex(AccountID const &account, std::uint32_t ticketSeq)
Definition: Indexes.cpp:116
ripple::ltTICKET
@ ltTICKET
Definition: LedgerFormats.h:68
ripple::ltCHECK
@ ltCHECK
Definition: LedgerFormats.h:85
Json::Value::asBool
bool asBool() const
Definition: json_value.cpp:619
ripple::Keylet::key
uint256 key
Definition: Keylet.h:41
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:73
ripple::keylet::escrow
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition: Indexes.cpp:319
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:439
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:134
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:310
ripple::RPC::GRPCContext
Definition: Context.h:70
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:194
ripple::ltDEPOSIT_PREAUTH
@ ltDEPOSIT_PREAUTH
Definition: LedgerFormats.h:87
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: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::ltRIPPLE_STATE
@ ltRIPPLE_STATE
Definition: LedgerFormats.h:66
ripple::base_uint::parseHex
bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:384
ripple::LedgerEntryType
LedgerEntryType
Ledger entry types.
Definition: LedgerFormats.h:36
ripple::base_uint< 256 >::fromVoid
static base_uint fromVoid(void const *data)
Definition: base_uint.h:223
ripple::Serializer::peekData
Blob const & peekData() const
Definition: Serializer.h:166
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
Json::Value::isIntegral
bool isIntegral() const
Definition: json_value.cpp:991
ripple::ltPAYCHAN
@ ltPAYCHAN
Definition: LedgerFormats.h:83
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:45
ripple::ltDIR_NODE
@ ltDIR_NODE
Directory node.
Definition: LedgerFormats.h:64
ripple::ltOFFER
@ ltOFFER
Definition: LedgerFormats.h:72
ripple::Serializer::getLength
int getLength() const
Definition: Serializer.h:197
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
Definition: LedgerFormats.h:53
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::string::data
T data(T... args)
ripple::RPC::ledgerFromRequest
Status ledgerFromRequest(T &ledger, GRPCContext< R > &context)
Definition: RPCHelpers.cpp:252
Json::Value
Represents a JSON value.
Definition: json_value.h:145
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469