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/impl/RPCHelpers.h>
30 
31 namespace ripple {
32 
33 // {
34 // ledger_hash : <ledger>
35 // ledger_index : <ledger_index>
36 // ...
37 // }
40 {
42  auto jvResult = RPC::lookupLedger(lpLedger, context);
43 
44  if (!lpLedger)
45  return jvResult;
46 
47  uint256 uNodeIndex;
48  bool bNodeBinary = false;
49  LedgerEntryType expectedType = ltANY;
50 
51  if (context.params.isMember(jss::index))
52  {
53  if (!uNodeIndex.parseHex(context.params[jss::index].asString()))
54  {
55  uNodeIndex = beast::zero;
56  jvResult[jss::error] = "malformedRequest";
57  }
58  }
59  else if (context.params.isMember(jss::account_root))
60  {
61  expectedType = ltACCOUNT_ROOT;
62  auto const account = parseBase58<AccountID>(
63  context.params[jss::account_root].asString());
64  if (!account || account->isZero())
65  jvResult[jss::error] = "malformedAddress";
66  else
67  uNodeIndex = keylet::account(*account).key;
68  }
69  else if (context.params.isMember(jss::check))
70  {
71  expectedType = ltCHECK;
72 
73  if (!uNodeIndex.parseHex(context.params[jss::check].asString()))
74  {
75  uNodeIndex = beast::zero;
76  jvResult[jss::error] = "malformedRequest";
77  }
78  }
79  else if (context.params.isMember(jss::deposit_preauth))
80  {
81  expectedType = ltDEPOSIT_PREAUTH;
82 
83  if (!context.params[jss::deposit_preauth].isObject())
84  {
85  if (!context.params[jss::deposit_preauth].isString() ||
86  !uNodeIndex.parseHex(
87  context.params[jss::deposit_preauth].asString()))
88  {
89  uNodeIndex = beast::zero;
90  jvResult[jss::error] = "malformedRequest";
91  }
92  }
93  else if (
94  !context.params[jss::deposit_preauth].isMember(jss::owner) ||
95  !context.params[jss::deposit_preauth][jss::owner].isString() ||
96  !context.params[jss::deposit_preauth].isMember(jss::authorized) ||
97  !context.params[jss::deposit_preauth][jss::authorized].isString())
98  {
99  jvResult[jss::error] = "malformedRequest";
100  }
101  else
102  {
103  auto const owner = parseBase58<AccountID>(
104  context.params[jss::deposit_preauth][jss::owner].asString());
105 
106  auto const authorized = parseBase58<AccountID>(
107  context.params[jss::deposit_preauth][jss::authorized]
108  .asString());
109 
110  if (!owner)
111  jvResult[jss::error] = "malformedOwner";
112  else if (!authorized)
113  jvResult[jss::error] = "malformedAuthorized";
114  else
115  uNodeIndex = keylet::depositPreauth(*owner, *authorized).key;
116  }
117  }
118  else if (context.params.isMember(jss::directory))
119  {
120  expectedType = ltDIR_NODE;
121  if (context.params[jss::directory].isNull())
122  {
123  jvResult[jss::error] = "malformedRequest";
124  }
125  else if (!context.params[jss::directory].isObject())
126  {
127  if (!uNodeIndex.parseHex(context.params[jss::directory].asString()))
128  {
129  uNodeIndex = beast::zero;
130  jvResult[jss::error] = "malformedRequest";
131  }
132  }
133  else if (
134  context.params[jss::directory].isMember(jss::sub_index) &&
135  !context.params[jss::directory][jss::sub_index].isIntegral())
136  {
137  jvResult[jss::error] = "malformedRequest";
138  }
139  else
140  {
141  std::uint64_t uSubIndex =
142  context.params[jss::directory].isMember(jss::sub_index)
143  ? context.params[jss::directory][jss::sub_index].asUInt()
144  : 0;
145 
146  if (context.params[jss::directory].isMember(jss::dir_root))
147  {
148  uint256 uDirRoot;
149 
150  if (context.params[jss::directory].isMember(jss::owner))
151  {
152  // May not specify both dir_root and owner.
153  jvResult[jss::error] = "malformedRequest";
154  }
155  else if (!uDirRoot.parseHex(
156  context.params[jss::directory][jss::dir_root]
157  .asString()))
158  {
159  uNodeIndex = beast::zero;
160  jvResult[jss::error] = "malformedRequest";
161  }
162  else
163  {
164  uNodeIndex = keylet::page(uDirRoot, uSubIndex).key;
165  }
166  }
167  else if (context.params[jss::directory].isMember(jss::owner))
168  {
169  auto const ownerID = parseBase58<AccountID>(
170  context.params[jss::directory][jss::owner].asString());
171 
172  if (!ownerID)
173  {
174  jvResult[jss::error] = "malformedAddress";
175  }
176  else
177  {
178  uNodeIndex =
179  keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key;
180  }
181  }
182  else
183  {
184  jvResult[jss::error] = "malformedRequest";
185  }
186  }
187  }
188  else if (context.params.isMember(jss::escrow))
189  {
190  expectedType = ltESCROW;
191  if (!context.params[jss::escrow].isObject())
192  {
193  if (!uNodeIndex.parseHex(context.params[jss::escrow].asString()))
194  {
195  uNodeIndex = beast::zero;
196  jvResult[jss::error] = "malformedRequest";
197  }
198  }
199  else if (
200  !context.params[jss::escrow].isMember(jss::owner) ||
201  !context.params[jss::escrow].isMember(jss::seq) ||
202  !context.params[jss::escrow][jss::seq].isIntegral())
203  {
204  jvResult[jss::error] = "malformedRequest";
205  }
206  else
207  {
208  auto const id = parseBase58<AccountID>(
209  context.params[jss::escrow][jss::owner].asString());
210  if (!id)
211  jvResult[jss::error] = "malformedOwner";
212  else
213  uNodeIndex =
215  *id, context.params[jss::escrow][jss::seq].asUInt())
216  .key;
217  }
218  }
219  else if (context.params.isMember(jss::offer))
220  {
221  expectedType = ltOFFER;
222  if (!context.params[jss::offer].isObject())
223  {
224  if (!uNodeIndex.parseHex(context.params[jss::offer].asString()))
225  {
226  uNodeIndex = beast::zero;
227  jvResult[jss::error] = "malformedRequest";
228  }
229  }
230  else if (
231  !context.params[jss::offer].isMember(jss::account) ||
232  !context.params[jss::offer].isMember(jss::seq) ||
233  !context.params[jss::offer][jss::seq].isIntegral())
234  {
235  jvResult[jss::error] = "malformedRequest";
236  }
237  else
238  {
239  auto const id = parseBase58<AccountID>(
240  context.params[jss::offer][jss::account].asString());
241  if (!id)
242  jvResult[jss::error] = "malformedAddress";
243  else
244  uNodeIndex =
246  *id, context.params[jss::offer][jss::seq].asUInt())
247  .key;
248  }
249  }
250  else if (context.params.isMember(jss::payment_channel))
251  {
252  expectedType = ltPAYCHAN;
253 
254  if (!uNodeIndex.parseHex(
255  context.params[jss::payment_channel].asString()))
256  {
257  uNodeIndex = beast::zero;
258  jvResult[jss::error] = "malformedRequest";
259  }
260  }
261  else if (context.params.isMember(jss::ripple_state))
262  {
263  expectedType = ltRIPPLE_STATE;
264  Currency uCurrency;
265  Json::Value jvRippleState = context.params[jss::ripple_state];
266 
267  if (!jvRippleState.isObject() ||
268  !jvRippleState.isMember(jss::currency) ||
269  !jvRippleState.isMember(jss::accounts) ||
270  !jvRippleState[jss::accounts].isArray() ||
271  2 != jvRippleState[jss::accounts].size() ||
272  !jvRippleState[jss::accounts][0u].isString() ||
273  !jvRippleState[jss::accounts][1u].isString() ||
274  (jvRippleState[jss::accounts][0u].asString() ==
275  jvRippleState[jss::accounts][1u].asString()))
276  {
277  jvResult[jss::error] = "malformedRequest";
278  }
279  else
280  {
281  auto const id1 = parseBase58<AccountID>(
282  jvRippleState[jss::accounts][0u].asString());
283  auto const id2 = parseBase58<AccountID>(
284  jvRippleState[jss::accounts][1u].asString());
285  if (!id1 || !id2)
286  {
287  jvResult[jss::error] = "malformedAddress";
288  }
289  else if (!to_currency(
290  uCurrency, jvRippleState[jss::currency].asString()))
291  {
292  jvResult[jss::error] = "malformedCurrency";
293  }
294  else
295  {
296  uNodeIndex = keylet::line(*id1, *id2, uCurrency).key;
297  }
298  }
299  }
300  else if (context.params.isMember(jss::ticket))
301  {
302  expectedType = ltTICKET;
303  if (!context.params[jss::ticket].isObject())
304  {
305  if (!uNodeIndex.parseHex(context.params[jss::ticket].asString()))
306  {
307  uNodeIndex = beast::zero;
308  jvResult[jss::error] = "malformedRequest";
309  }
310  }
311  else if (
312  !context.params[jss::ticket].isMember(jss::account) ||
313  !context.params[jss::ticket].isMember(jss::ticket_seq) ||
314  !context.params[jss::ticket][jss::ticket_seq].isIntegral())
315  {
316  jvResult[jss::error] = "malformedRequest";
317  }
318  else
319  {
320  auto const id = parseBase58<AccountID>(
321  context.params[jss::ticket][jss::account].asString());
322  if (!id)
323  jvResult[jss::error] = "malformedAddress";
324  else
325  uNodeIndex = getTicketIndex(
326  *id, context.params[jss::ticket][jss::ticket_seq].asUInt());
327  }
328  }
329  else
330  {
331  jvResult[jss::error] = "unknownOption";
332  }
333 
334  if (uNodeIndex.isNonZero())
335  {
336  auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex));
337  if (context.params.isMember(jss::binary))
338  bNodeBinary = context.params[jss::binary].asBool();
339 
340  if (!sleNode)
341  {
342  // Not found.
343  jvResult[jss::error] = "entryNotFound";
344  }
345  else if (
346  (expectedType != ltANY) && (expectedType != sleNode->getType()))
347  {
348  jvResult[jss::error] = "malformedRequest";
349  }
350  else if (bNodeBinary)
351  {
352  Serializer s;
353 
354  sleNode->add(s);
355 
356  jvResult[jss::node_binary] = strHex(s.peekData());
357  jvResult[jss::index] = to_string(uNodeIndex);
358  }
359  else
360  {
361  jvResult[jss::node] = sleNode->getJson(JsonOptions::none);
362  jvResult[jss::index] = to_string(uNodeIndex);
363  }
364  }
365 
366  return jvResult;
367 }
368 
369 } // 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:39
Json::Value::isObject
bool isObject() const
Definition: json_value.cpp:1027
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
ripple::ltESCROW
@ ltESCROW
Definition: LedgerFormats.h:80
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:42
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:487
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::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
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
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::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::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
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