rippled
AccountChannels.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/ledger/ReadView.h>
22 #include <ripple/ledger/View.h>
23 #include <ripple/net/RPCErr.h>
24 #include <ripple/protocol/ErrorCodes.h>
25 #include <ripple/protocol/PublicKey.h>
26 #include <ripple/protocol/STAccount.h>
27 #include <ripple/protocol/jss.h>
28 #include <ripple/resource/Fees.h>
29 #include <ripple/rpc/Context.h>
30 #include <ripple/rpc/impl/RPCHelpers.h>
31 #include <ripple/rpc/impl/Tuning.h>
32 namespace ripple {
33 
34 void
35 addChannel(Json::Value& jsonLines, SLE const& line)
36 {
37  Json::Value& jDst(jsonLines.append(Json::objectValue));
38  jDst[jss::channel_id] = to_string(line.key());
39  jDst[jss::account] = to_string(line[sfAccount]);
40  jDst[jss::destination_account] = to_string(line[sfDestination]);
41  jDst[jss::amount] = line[sfAmount].getText();
42  jDst[jss::balance] = line[sfBalance].getText();
43  if (publicKeyType(line[sfPublicKey]))
44  {
45  PublicKey const pk(line[sfPublicKey]);
46  jDst[jss::public_key] = toBase58(TokenType::AccountPublic, pk);
47  jDst[jss::public_key_hex] = strHex(pk);
48  }
49  jDst[jss::settle_delay] = line[sfSettleDelay];
50  if (auto const& v = line[~sfExpiration])
51  jDst[jss::expiration] = *v;
52  if (auto const& v = line[~sfCancelAfter])
53  jDst[jss::cancel_after] = *v;
54  if (auto const& v = line[~sfSourceTag])
55  jDst[jss::source_tag] = *v;
56  if (auto const& v = line[~sfDestinationTag])
57  jDst[jss::destination_tag] = *v;
58 }
59 
60 // {
61 // account: <account>
62 // ledger_hash : <ledger>
63 // ledger_index : <ledger_index>
64 // limit: integer // optional
65 // marker: opaque // optional, resume previous query
66 // }
69 {
70  auto const& params(context.params);
71  if (!params.isMember(jss::account))
72  return RPC::missing_field_error(jss::account);
73 
75  auto result = RPC::lookupLedger(ledger, context);
76  if (!ledger)
77  return result;
78 
79  auto id = parseBase58<AccountID>(params[jss::account].asString());
80  if (!id)
81  {
82  return rpcError(rpcACT_MALFORMED);
83  }
84  AccountID const accountID{std::move(id.value())};
85 
86  if (!ledger->exists(keylet::account(accountID)))
87  return rpcError(rpcACT_NOT_FOUND);
88 
89  std::string strDst;
90  if (params.isMember(jss::destination_account))
91  strDst = params[jss::destination_account].asString();
92 
93  auto const raDstAccount = [&]() -> std::optional<AccountID> {
94  return strDst.empty() ? std::nullopt : parseBase58<AccountID>(strDst);
95  }();
96  if (!strDst.empty() && !raDstAccount)
97  return rpcError(rpcACT_MALFORMED);
98 
99  unsigned int limit;
100  if (auto err = readLimitField(limit, RPC::Tuning::accountChannels, context))
101  return *err;
102 
103  if (limit == 0u)
104  return rpcError(rpcINVALID_PARAMS);
105 
106  Json::Value jsonChannels{Json::arrayValue};
107  struct VisitData
108  {
110  AccountID const& accountID;
111  std::optional<AccountID> const& raDstAccount;
112  };
113  VisitData visitData = {{}, accountID, raDstAccount};
114  visitData.items.reserve(limit);
115  uint256 startAfter = beast::zero;
116  std::uint64_t startHint = 0;
117 
118  if (params.isMember(jss::marker))
119  {
120  if (!params[jss::marker].isString())
121  return RPC::expected_field_error(jss::marker, "string");
122 
123  // Marker is composed of a comma separated index and start hint. The
124  // former will be read as hex, and the latter using boost lexical cast.
125  std::stringstream marker(params[jss::marker].asString());
126  std::string value;
127  if (!std::getline(marker, value, ','))
128  return rpcError(rpcINVALID_PARAMS);
129 
130  if (!startAfter.parseHex(value))
131  return rpcError(rpcINVALID_PARAMS);
132 
133  if (!std::getline(marker, value, ','))
134  return rpcError(rpcINVALID_PARAMS);
135 
136  try
137  {
138  startHint = boost::lexical_cast<std::uint64_t>(value);
139  }
140  catch (boost::bad_lexical_cast&)
141  {
142  return rpcError(rpcINVALID_PARAMS);
143  }
144 
145  // We then must check if the object pointed to by the marker is actually
146  // owned by the account in the request.
147  auto const sle = ledger->read({ltANY, startAfter});
148 
149  if (!sle)
150  return rpcError(rpcINVALID_PARAMS);
151 
152  if (!RPC::isRelatedToAccount(*ledger, sle, accountID))
153  return rpcError(rpcINVALID_PARAMS);
154  }
155 
156  auto count = 0;
157  std::optional<uint256> marker = {};
158  std::uint64_t nextHint = 0;
159  if (!forEachItemAfter(
160  *ledger,
161  accountID,
162  startAfter,
163  startHint,
164  limit + 1,
165  [&visitData, &accountID, &count, &limit, &marker, &nextHint](
166  std::shared_ptr<SLE const> const& sleCur) {
167  if (!sleCur)
168  {
169  assert(false);
170  return false;
171  }
172 
173  if (++count == limit)
174  {
175  marker = sleCur->key();
176  nextHint = RPC::getStartHint(sleCur, visitData.accountID);
177  }
178 
179  if (count <= limit && sleCur->getType() == ltPAYCHAN &&
180  (*sleCur)[sfAccount] == accountID &&
181  (!visitData.raDstAccount ||
182  *visitData.raDstAccount == (*sleCur)[sfDestination]))
183  {
184  visitData.items.emplace_back(sleCur);
185  }
186 
187  return true;
188  }))
189  {
190  return rpcError(rpcINVALID_PARAMS);
191  }
192 
193  // Both conditions need to be checked because marker is set on the limit-th
194  // item, but if there is no item on the limit + 1 iteration, then there is
195  // no need to return a marker.
196  if (count == limit + 1 && marker)
197  {
198  result[jss::limit] = limit;
199  result[jss::marker] =
200  to_string(*marker) + "," + std::to_string(nextHint);
201  }
202 
203  result[jss::account] = toBase58(accountID);
204 
205  for (auto const& item : visitData.items)
206  addChannel(jsonChannels, *item);
207 
209  result[jss::channels] = std::move(jsonChannels);
210  return result;
211 }
212 
213 } // 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
ripple::sfSourceTag
const SF_UINT32 sfSourceTag
ripple::STLedgerEntry
Definition: STLedgerEntry.h:30
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::sfDestination
const SF_ACCOUNT sfDestination
ripple::sfAmount
const SF_AMOUNT sfAmount
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::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
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::sfExpiration
const SF_UINT32 sfExpiration
ripple::RPC::missing_field_error
Json::Value missing_field_error(std::string const &name)
Definition: ErrorCodes.h:262
ripple::publicKeyType
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
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:82
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::sfSettleDelay
const SF_UINT32 sfSettleDelay
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
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::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
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::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::sfDestinationTag
const SF_UINT32 sfDestinationTag
ripple::TokenType::AccountPublic
@ AccountPublic
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::RPC::Tuning::accountChannels
static constexpr LimitRange accountChannels
Limits for the account_channels command.
Definition: rpc/impl/Tuning.h:40
std::string::empty
T empty(T... args)
ripple::sfCancelAfter
const SF_UINT32 sfCancelAfter
std::optional
ripple::doAccountChannels
Json::Value doAccountChannels(RPC::JsonContext &context)
Definition: AccountChannels.cpp:68
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::sfAccount
const SF_ACCOUNT sfAccount
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
ripple::addChannel
void addChannel(Json::Value &jsonLines, SLE const &line)
Definition: AccountChannels.cpp:35
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::sfPublicKey
const SF_VL sfPublicKey
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
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