rippled
AccountLines.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/app/paths/TrustLine.h>
22 #include <ripple/ledger/ReadView.h>
23 #include <ripple/net/RPCErr.h>
24 #include <ripple/protocol/ErrorCodes.h>
25 #include <ripple/protocol/jss.h>
26 #include <ripple/resource/Fees.h>
27 #include <ripple/rpc/Context.h>
28 #include <ripple/rpc/impl/RPCHelpers.h>
29 #include <ripple/rpc/impl/Tuning.h>
30 
31 namespace ripple {
32 
33 void
34 addLine(Json::Value& jsonLines, RPCTrustLine const& line)
35 {
36  STAmount const& saBalance(line.getBalance());
37  STAmount const& saLimit(line.getLimit());
38  STAmount const& saLimitPeer(line.getLimitPeer());
39  Json::Value& jPeer(jsonLines.append(Json::objectValue));
40 
41  jPeer[jss::account] = to_string(line.getAccountIDPeer());
42  // Amount reported is positive if current account holds other
43  // account's IOUs.
44  //
45  // Amount reported is negative if other account holds current
46  // account's IOUs.
47  jPeer[jss::balance] = saBalance.getText();
48  jPeer[jss::currency] = to_string(saBalance.issue().currency);
49  jPeer[jss::limit] = saLimit.getText();
50  jPeer[jss::limit_peer] = saLimitPeer.getText();
51  jPeer[jss::quality_in] = line.getQualityIn().value;
52  jPeer[jss::quality_out] = line.getQualityOut().value;
53  if (line.getAuth())
54  jPeer[jss::authorized] = true;
55  if (line.getAuthPeer())
56  jPeer[jss::peer_authorized] = true;
57  if (line.getNoRipple() || !line.getDefaultRipple())
58  jPeer[jss::no_ripple] = line.getNoRipple();
59  if (line.getNoRipplePeer() || !line.getDefaultRipple())
60  jPeer[jss::no_ripple_peer] = line.getNoRipplePeer();
61  if (line.getFreeze())
62  jPeer[jss::freeze] = true;
63  if (line.getFreezePeer())
64  jPeer[jss::freeze_peer] = true;
65 }
66 
67 // {
68 // account: <account>
69 // ledger_hash : <ledger>
70 // ledger_index : <ledger_index>
71 // limit: integer // optional
72 // marker: opaque // optional, resume previous query
73 // ignore_default: bool // do not return lines in default state (on
74 // this account's side)
75 // }
78 {
79  auto const& params(context.params);
80  if (!params.isMember(jss::account))
81  return RPC::missing_field_error(jss::account);
82 
84  auto result = RPC::lookupLedger(ledger, context);
85  if (!ledger)
86  return result;
87 
88  auto id = parseBase58<AccountID>(params[jss::account].asString());
89  if (!id)
90  {
92  return result;
93  }
94  auto const accountID{std::move(id.value())};
95 
96  if (!ledger->exists(keylet::account(accountID)))
97  return rpcError(rpcACT_NOT_FOUND);
98 
99  std::string strPeer;
100  if (params.isMember(jss::peer))
101  strPeer = params[jss::peer].asString();
102 
103  auto const raPeerAccount = [&]() -> std::optional<AccountID> {
104  return strPeer.empty() ? std::nullopt : parseBase58<AccountID>(strPeer);
105  }();
106  if (!strPeer.empty() && !raPeerAccount)
107  {
109  return result;
110  }
111 
112  unsigned int limit;
113  if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context))
114  return *err;
115 
116  if (limit == 0)
117  return rpcError(rpcINVALID_PARAMS);
118 
119  // this flag allows the requester to ask incoming trustlines in default
120  // state be omitted
121  bool ignoreDefault = params.isMember(jss::ignore_default) &&
122  params[jss::ignore_default].asBool();
123 
124  Json::Value& jsonLines(result[jss::lines] = Json::arrayValue);
125  struct VisitData
126  {
128  AccountID const& accountID;
129  std::optional<AccountID> const& raPeerAccount;
130  bool ignoreDefault;
131  uint32_t foundCount;
132  };
133  VisitData visitData = {{}, accountID, raPeerAccount, ignoreDefault, 0};
134  uint256 startAfter = beast::zero;
135  std::uint64_t startHint = 0;
136 
137  if (params.isMember(jss::marker))
138  {
139  if (!params[jss::marker].isString())
140  return RPC::expected_field_error(jss::marker, "string");
141 
142  // Marker is composed of a comma separated index and start hint. The
143  // former will be read as hex, and the latter using boost lexical cast.
144  std::stringstream marker(params[jss::marker].asString());
145  std::string value;
146  if (!std::getline(marker, value, ','))
147  return rpcError(rpcINVALID_PARAMS);
148 
149  if (!startAfter.parseHex(value))
150  return rpcError(rpcINVALID_PARAMS);
151 
152  if (!std::getline(marker, value, ','))
153  return rpcError(rpcINVALID_PARAMS);
154 
155  try
156  {
157  startHint = boost::lexical_cast<std::uint64_t>(value);
158  }
159  catch (boost::bad_lexical_cast&)
160  {
161  return rpcError(rpcINVALID_PARAMS);
162  }
163 
164  // We then must check if the object pointed to by the marker is actually
165  // owned by the account in the request.
166  auto const sle = ledger->read({ltANY, startAfter});
167 
168  if (!sle)
169  return rpcError(rpcINVALID_PARAMS);
170 
171  if (!RPC::isRelatedToAccount(*ledger, sle, accountID))
172  return rpcError(rpcINVALID_PARAMS);
173  }
174 
175  auto count = 0;
176  std::optional<uint256> marker = {};
177  std::uint64_t nextHint = 0;
178  {
179  if (!forEachItemAfter(
180  *ledger,
181  accountID,
182  startAfter,
183  startHint,
184  limit + 1,
185  [&visitData, &count, &marker, &limit, &nextHint](
186  std::shared_ptr<SLE const> const& sleCur) {
187  if (!sleCur)
188  {
189  assert(false);
190  return false;
191  }
192 
193  if (++count == limit)
194  {
195  marker = sleCur->key();
196  nextHint =
197  RPC::getStartHint(sleCur, visitData.accountID);
198  }
199 
200  if (sleCur->getType() != ltRIPPLE_STATE)
201  return true;
202 
203  bool ignore = false;
204  if (visitData.ignoreDefault)
205  {
206  if (sleCur->getFieldAmount(sfLowLimit).getIssuer() ==
207  visitData.accountID)
208  ignore =
209  !(sleCur->getFieldU32(sfFlags) & lsfLowReserve);
210  else
211  ignore = !(
212  sleCur->getFieldU32(sfFlags) & lsfHighReserve);
213  }
214 
215  if (!ignore && count <= limit)
216  {
217  auto const line =
218  RPCTrustLine::makeItem(visitData.accountID, sleCur);
219 
220  if (line &&
221  (!visitData.raPeerAccount ||
222  *visitData.raPeerAccount ==
223  line->getAccountIDPeer()))
224  {
225  visitData.items.emplace_back(*line);
226  }
227  }
228 
229  return true;
230  }))
231  {
232  return rpcError(rpcINVALID_PARAMS);
233  }
234  }
235 
236  // Both conditions need to be checked because marker is set on the limit-th
237  // item, but if there is no item on the limit + 1 iteration, then there is
238  // no need to return a marker.
239  if (count == limit + 1 && marker)
240  {
241  result[jss::limit] = limit;
242  result[jss::marker] =
243  to_string(*marker) + "," + std::to_string(nextHint);
244  }
245 
246  result[jss::account] = toBase58(accountID);
247 
248  for (auto const& item : visitData.items)
249  addLine(jsonLines, item);
250 
252  return result;
253 }
254 
255 } // namespace ripple
ripple::STLedgerEntry::key
uint256 const & key() const
Returns the 'key' (or 'index') of this item.
Definition: STLedgerEntry.h:113
ripple::RPC::JsonContext
Definition: Context.h:53
std::string
STL class.
std::shared_ptr
STL class.
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
ripple::rpcError
Json::Value rpcError(int iError)
Definition: RPCErr.cpp:29
ripple::ltANY
@ ltANY
A special type, matching any ledger entry type.
Definition: LedgerFormats.h:176
ripple::forEachItemAfter
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
Definition: View.cpp:394
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:347
ripple::lsfLowReserve
@ lsfLowReserve
Definition: LedgerFormats.h:252
ripple::Resource::feeMediumBurdenRPC
const Charge feeMediumBurdenRPC
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::RPC::Context::loadType
Resource::Charge & loadType
Definition: Context.h:43
std::vector
STL class.
ripple::STAmount::getText
std::string getText() const override
Definition: STAmount.cpp:571
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
ripple::RPCTrustLine
Definition: TrustLine.h:204
std::stringstream
STL class.
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::RPC::expected_field_error
Json::Value expected_field_error(std::string const &name, std::string const &type)
Definition: ErrorCodes.h:328
ripple::STAmount::getIssuer
AccountID const & getIssuer() const
Definition: STAmount.h:359
ripple::RPC::missing_field_error
Json::Value missing_field_error(std::string const &name)
Definition: ErrorCodes.h:262
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:82
ripple::sfLowLimit
const SF_AMOUNT sfLowLimit
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::RPC::Tuning::accountLines
static constexpr LimitRange accountLines
Limits for the account_lines command.
Definition: rpc/impl/Tuning.h:37
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
ripple::rpcACT_NOT_FOUND
@ rpcACT_NOT_FOUND
Definition: ErrorCodes.h:70
std::to_string
T to_string(T... args)
ripple::STAmount
Definition: STAmount.h:45
ripple::ReadView::exists
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
std::uint64_t
ripple::STLedgerEntry::getType
LedgerEntryType getType() const
Definition: STLedgerEntry.h:119
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::rpcACT_MALFORMED
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
std::getline
T getline(T... args)
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::RPC::getStartHint
std::uint64_t getStartHint(std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Gets the start hint for traversing account objects.
Definition: RPCHelpers.cpp:96
ripple::RPCTrustLine::makeItem
static std::optional< RPCTrustLine > makeItem(AccountID const &accountID, std::shared_ptr< SLE const > const &sle)
Definition: TrustLine.cpp:110
std::string::empty
T empty(T... args)
std::optional
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::STObject::getFieldU32
std::uint32_t getFieldU32(SField const &field) const
Definition: STObject.cpp:559
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::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
ripple::lsfHighReserve
@ lsfHighReserve
Definition: LedgerFormats.h:253
ripple::RPC::inject_error
void inject_error(error_code_i code, JsonValue &json)
Add or update the json update to reflect the error code.
Definition: ErrorCodes.h:212
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:603
ripple::RPC::isRelatedToAccount
bool isRelatedToAccount(ReadView const &ledger, std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Tests if a SLE is owned by accountID.
Definition: RPCHelpers.cpp:113
ripple::addLine
void addLine(Json::Value &jsonLines, RPCTrustLine const &line)
Definition: AccountLines.cpp:34
ripple::doAccountLines
Json::Value doAccountLines(RPC::JsonContext &context)
Definition: AccountLines.cpp:77
Json::Value
Represents a JSON value.
Definition: json_value.h:145