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/RippleState.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 struct VisitData
34 {
37  bool hasPeer;
39 
41  uint32_t foundCount;
43 };
44 
45 void
46 addLine(Json::Value& jsonLines, RippleState const& line)
47 {
48  STAmount const& saBalance(line.getBalance());
49  STAmount const& saLimit(line.getLimit());
50  STAmount const& saLimitPeer(line.getLimitPeer());
51  Json::Value& jPeer(jsonLines.append(Json::objectValue));
52 
53  jPeer[jss::account] = to_string(line.getAccountIDPeer());
54  // Amount reported is positive if current account holds other
55  // account's IOUs.
56  //
57  // Amount reported is negative if other account holds current
58  // account's IOUs.
59  jPeer[jss::balance] = saBalance.getText();
60  jPeer[jss::currency] = to_string(saBalance.issue().currency);
61  jPeer[jss::limit] = saLimit.getText();
62  jPeer[jss::limit_peer] = saLimitPeer.getText();
63  jPeer[jss::quality_in] = line.getQualityIn().value;
64  jPeer[jss::quality_out] = line.getQualityOut().value;
65  if (line.getAuth())
66  jPeer[jss::authorized] = true;
67  if (line.getAuthPeer())
68  jPeer[jss::peer_authorized] = true;
69  if (line.getNoRipple() || !line.getDefaultRipple())
70  jPeer[jss::no_ripple] = line.getNoRipple();
71  if (line.getNoRipplePeer() || !line.getDefaultRipple())
72  jPeer[jss::no_ripple_peer] = line.getNoRipplePeer();
73  if (line.getFreeze())
74  jPeer[jss::freeze] = true;
75  if (line.getFreezePeer())
76  jPeer[jss::freeze_peer] = true;
77 }
78 
79 // {
80 // account: <account>|<account_public_key>
81 // ledger_hash : <ledger>
82 // ledger_index : <ledger_index>
83 // limit: integer // optional
84 // marker: opaque // optional, resume previous query
85 // ignore_default: bool // do not return lines in default state (on
86 // this account's side)
87 // }
90 {
91  auto const& params(context.params);
92  if (!params.isMember(jss::account))
93  return RPC::missing_field_error(jss::account);
94 
96  auto result = RPC::lookupLedger(ledger, context);
97  if (!ledger)
98  return result;
99 
100  std::string strIdent(params[jss::account].asString());
101  AccountID accountID;
102 
103  if (auto jv = RPC::accountFromString(accountID, strIdent))
104  {
105  for (auto it = jv.begin(); it != jv.end(); ++it)
106  result[it.memberName()] = *it;
107  return result;
108  }
109 
110  if (!ledger->exists(keylet::account(accountID)))
111  return rpcError(rpcACT_NOT_FOUND);
112 
113  std::string strPeer;
114  if (params.isMember(jss::peer))
115  strPeer = params[jss::peer].asString();
116  auto hasPeer = !strPeer.empty();
117 
118  AccountID raPeerAccount;
119  if (hasPeer)
120  {
121  if (auto jv = RPC::accountFromString(raPeerAccount, strPeer))
122  {
123  for (auto it = jv.begin(); it != jv.end(); ++it)
124  result[it.memberName()] = *it;
125  return result;
126  }
127  }
128 
129  unsigned int limit;
130  if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context))
131  return *err;
132 
133  if (limit == 0)
134  return rpcError(rpcINVALID_PARAMS);
135 
136  // this flag allows the requester to ask incoming trustlines in default
137  // state be omitted
138  bool ignoreDefault = params.isMember(jss::ignore_default) &&
139  params[jss::ignore_default].asBool();
140 
141  Json::Value& jsonLines(result[jss::lines] = Json::arrayValue);
142  VisitData visitData = {
143  {}, accountID, hasPeer, raPeerAccount, ignoreDefault, 0, nullptr};
144  uint256 startAfter = beast::zero;
145  std::uint64_t startHint = 0;
146 
147  if (params.isMember(jss::marker))
148  {
149  if (!params[jss::marker].isString())
150  return RPC::expected_field_error(jss::marker, "string");
151 
152  // Marker is composed of a comma separated index and start hint. The
153  // former will be read as hex, and the latter using boost lexical cast.
154  std::stringstream marker(params[jss::marker].asString());
155  std::string value;
156  if (!std::getline(marker, value, ','))
157  return rpcError(rpcINVALID_PARAMS);
158 
159  if (!startAfter.parseHex(value))
160  return rpcError(rpcINVALID_PARAMS);
161 
162  if (!std::getline(marker, value, ','))
163  return rpcError(rpcINVALID_PARAMS);
164 
165  try
166  {
167  startHint = boost::lexical_cast<std::uint64_t>(value);
168  }
169  catch (boost::bad_lexical_cast&)
170  {
171  return rpcError(rpcINVALID_PARAMS);
172  }
173 
174  // We then must check if the object pointed to by the marker is actually
175  // owned by the account in the request.
176  auto const sle = ledger->read({ltANY, startAfter});
177 
178  if (!sle)
179  return rpcError(rpcINVALID_PARAMS);
180 
181  if (!RPC::isOwnedByAccount(*ledger, sle, accountID))
182  return rpcError(rpcINVALID_PARAMS);
183  }
184 
185  auto count = 0;
186  std::optional<uint256> marker = {};
187  std::uint64_t nextHint = 0;
188  {
189  if (!forEachItemAfter(
190  *ledger,
191  accountID,
192  startAfter,
193  startHint,
194  limit + 1,
195  [&visitData, &count, &marker, &limit, &nextHint](
196  std::shared_ptr<SLE const> const& sleCur) {
197  bool ignore = false;
198  if (visitData.ignoreDefault)
199  {
200  if (sleCur->getFieldAmount(sfLowLimit).getIssuer() ==
201  visitData.accountID)
202  ignore =
203  !(sleCur->getFieldU32(sfFlags) & lsfLowReserve);
204  else
205  ignore = !(
206  sleCur->getFieldU32(sfFlags) & lsfHighReserve);
207  }
208 
209  if (!sleCur)
210  {
211  assert(false);
212  return false;
213  }
214 
215  if (++count == limit)
216  {
217  marker = sleCur->key();
218  nextHint =
219  RPC::getStartHint(sleCur, visitData.accountID);
220  }
221 
222  if (!ignore && count <= limit)
223  {
224  auto const line =
225  RippleState::makeItem(visitData.accountID, sleCur);
226 
227  if (line != nullptr &&
228  (!visitData.hasPeer ||
229  visitData.raPeerAccount ==
230  line->getAccountIDPeer()))
231  {
232  visitData.items.emplace_back(line);
233  }
234  }
235 
236  return true;
237  }))
238  {
239  return rpcError(rpcINVALID_PARAMS);
240  }
241  }
242 
243  // Both conditions need to be checked because marker is set on the limit-th
244  // item, but if there is no item on the limit + 1 iteration, then there is
245  // no need to return a marker.
246  if (count == limit + 1 && marker)
247  {
248  result[jss::limit] = limit;
249  result[jss::marker] =
250  to_string(*marker) + "," + std::to_string(nextHint);
251  }
252 
253  result[jss::account] = context.app.accountIDCache().toBase58(accountID);
254 
255  for (auto const& item : visitData.items)
256  addLine(jsonLines, *item.get());
257 
259  return result;
260 }
261 
262 } // namespace ripple
ripple::RippleState
Wraps a trust line SLE for convenience.
Definition: RippleState.h:39
ripple::STLedgerEntry::key
uint256 const & key() const
Returns the 'key' (or 'index') of this item.
Definition: STLedgerEntry.h:113
ripple::VisitData::hasPeer
bool hasPeer
Definition: AccountLines.cpp:37
ripple::RPC::JsonContext
Definition: Context.h:53
std::string
STL class.
std::shared_ptr< RippleState >
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:164
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:338
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
Json::Value::get
Value get(UInt index, const Value &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
Definition: json_value.cpp:834
std::vector
STL class.
ripple::VisitData::lastFound
RippleState::pointer lastFound
Definition: AccountLines.cpp:42
ripple::STAmount::getText
std::string getText() const override
Definition: STAmount.cpp:530
ripple::VisitData::foundCount
uint32_t foundCount
Definition: AccountLines.cpp:41
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
ripple::VisitData::raPeerAccount
AccountID const & raPeerAccount
Definition: AccountLines.cpp:38
std::stringstream
STL class.
ripple::VisitData::ignoreDefault
bool ignoreDefault
Definition: AccountLines.cpp:40
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:582
ripple::VisitData
Definition: AccountLines.cpp:33
ripple::Application::accountIDCache
virtual AccountIDCache const & accountIDCache() const =0
ripple::RPC::expected_field_error
Json::Value expected_field_error(std::string const &name, std::string const &type)
Definition: ErrorCodes.h:309
ripple::RPC::missing_field_error
Json::Value missing_field_error(std::string const &name)
Definition: ErrorCodes.h:243
ripple::base_uint< 160, detail::AccountIDTag >
ripple::VisitData::accountID
AccountID const & accountID
Definition: AccountLines.cpp:36
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:130
ripple::RPC::isOwnedByAccount
bool isOwnedByAccount(ReadView const &ledger, std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Tests if a SLE is owned by accountID.
Definition: RPCHelpers.cpp:111
ripple::RippleState::makeItem
static RippleState::pointer makeItem(AccountID const &accountID, std::shared_ptr< SLE const > sle)
Definition: RippleState.cpp:29
ripple::rpcACT_NOT_FOUND
@ rpcACT_NOT_FOUND
Definition: ErrorCodes.h:70
std::to_string
T to_string(T... args)
ripple::STAmount
Definition: STAmount.h:43
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
ripple::ReadView::exists
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
std::uint64_t
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::addLine
void addLine(Json::Value &jsonLines, RippleState const &line)
Definition: AccountLines.cpp:46
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
std::getline
T getline(T... args)
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:94
ripple::VisitData::items
std::vector< RippleState::pointer > items
Definition: AccountLines.cpp:35
std::string::empty
T empty(T... args)
ripple::forEachItemAfter
bool forEachItemAfter(ReadView const &view, AccountID const &id, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> f)
Iterate all items after an item in an owner directory.
Definition: View.cpp:398
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:38
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::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
ripple::RPC::accountFromString
Json::Value accountFromString(AccountID &result, std::string const &strIdent, bool bStrict)
Definition: RPCHelpers.cpp:84
ripple::doAccountLines
Json::Value doAccountLines(RPC::JsonContext &context)
Definition: AccountLines.cpp:89
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::AccountIDCache::toBase58
std::string toBase58(AccountID const &) const
Return ripple::toBase58 for the AccountID.
Definition: AccountID.cpp:134