diff --git a/AccountChannels_8cpp_source.html b/AccountChannels_8cpp_source.html index 88b270b9d1..5c990614d2 100644 --- a/AccountChannels_8cpp_source.html +++ b/AccountChannels_8cpp_source.html @@ -129,7 +129,7 @@ $(function() {
58 }
59 
60 // {
-
61 // account: <account>|<account_public_key>
+
61 // account: <account>
62 // ledger_hash : <ledger>
63 // ledger_index : <ledger_index>
64 // limit: integer // optional
@@ -147,143 +147,141 @@ $(function() {
76  if (!ledger)
77  return result;
78 
-
79  std::string strIdent(params[jss::account].asString());
-
80  AccountID accountID;
-
81 
-
82  if (auto const err = RPC::accountFromString(accountID, strIdent))
-
83  return err;
-
84 
-
85  if (!ledger->exists(keylet::account(accountID)))
-
86  return rpcError(rpcACT_NOT_FOUND);
-
87 
-
88  std::string strDst;
-
89  if (params.isMember(jss::destination_account))
-
90  strDst = params[jss::destination_account].asString();
-
91  auto hasDst = !strDst.empty();
+
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  AccountID raDstAccount;
-
94  if (hasDst)
-
95  {
-
96  if (auto const err = RPC::accountFromString(raDstAccount, strDst))
-
97  return err;
-
98  }
-
99 
-
100  unsigned int limit;
-
101  if (auto err = readLimitField(limit, RPC::Tuning::accountChannels, context))
-
102  return *err;
-
103 
-
104  if (limit == 0u)
-
105  return rpcError(rpcINVALID_PARAMS);
-
106 
-
107  Json::Value jsonChannels{Json::arrayValue};
-
108  struct VisitData
-
109  {
-
110  std::vector<std::shared_ptr<SLE const>> items;
-
111  AccountID const& accountID;
-
112  bool hasDst;
-
113  AccountID const& raDstAccount;
-
114  };
-
115  VisitData visitData = {{}, accountID, hasDst, raDstAccount};
-
116  visitData.items.reserve(limit);
-
117  uint256 startAfter = beast::zero;
-
118  std::uint64_t startHint = 0;
-
119 
-
120  if (params.isMember(jss::marker))
-
121  {
-
122  if (!params[jss::marker].isString())
-
123  return RPC::expected_field_error(jss::marker, "string");
-
124 
-
125  // Marker is composed of a comma separated index and start hint. The
-
126  // former will be read as hex, and the latter using boost lexical cast.
-
127  std::stringstream marker(params[jss::marker].asString());
-
128  std::string value;
-
129  if (!std::getline(marker, value, ','))
-
130  return rpcError(rpcINVALID_PARAMS);
-
131 
-
132  if (!startAfter.parseHex(value))
-
133  return rpcError(rpcINVALID_PARAMS);
-
134 
-
135  if (!std::getline(marker, value, ','))
-
136  return rpcError(rpcINVALID_PARAMS);
-
137 
-
138  try
-
139  {
-
140  startHint = boost::lexical_cast<std::uint64_t>(value);
-
141  }
-
142  catch (boost::bad_lexical_cast&)
-
143  {
-
144  return rpcError(rpcINVALID_PARAMS);
-
145  }
-
146 
-
147  // We then must check if the object pointed to by the marker is actually
-
148  // owned by the account in the request.
-
149  auto const sle = ledger->read({ltANY, startAfter});
-
150 
-
151  if (!sle)
-
152  return rpcError(rpcINVALID_PARAMS);
-
153 
-
154  if (!RPC::isRelatedToAccount(*ledger, sle, accountID))
-
155  return rpcError(rpcINVALID_PARAMS);
-
156  }
-
157 
-
158  auto count = 0;
-
159  std::optional<uint256> marker = {};
-
160  std::uint64_t nextHint = 0;
-
161  if (!forEachItemAfter(
-
162  *ledger,
-
163  accountID,
-
164  startAfter,
-
165  startHint,
-
166  limit + 1,
-
167  [&visitData, &accountID, &count, &limit, &marker, &nextHint](
-
168  std::shared_ptr<SLE const> const& sleCur) {
-
169  if (!sleCur)
-
170  {
-
171  assert(false);
-
172  return false;
-
173  }
-
174 
-
175  if (++count == limit)
-
176  {
-
177  marker = sleCur->key();
-
178  nextHint = RPC::getStartHint(sleCur, visitData.accountID);
-
179  }
-
180 
-
181  if (count <= limit && sleCur->getType() == ltPAYCHAN &&
-
182  (*sleCur)[sfAccount] == accountID &&
-
183  (!visitData.hasDst ||
-
184  visitData.raDstAccount == (*sleCur)[sfDestination]))
-
185  {
-
186  visitData.items.emplace_back(sleCur);
-
187  }
-
188 
-
189  return true;
-
190  }))
-
191  {
-
192  return rpcError(rpcINVALID_PARAMS);
-
193  }
-
194 
-
195  // Both conditions need to be checked because marker is set on the limit-th
-
196  // item, but if there is no item on the limit + 1 iteration, then there is
-
197  // no need to return a marker.
-
198  if (count == limit + 1 && marker)
-
199  {
-
200  result[jss::limit] = limit;
-
201  result[jss::marker] =
-
202  to_string(*marker) + "," + std::to_string(nextHint);
-
203  }
+
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  {
+
109  std::vector<std::shared_ptr<SLE const>> items;
+
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  result[jss::account] = toBase58(accountID);
-
206 
-
207  for (auto const& item : visitData.items)
-
208  addChannel(jsonChannels, *item);
-
209 
-
210  context.loadType = Resource::feeMediumBurdenRPC;
-
211  result[jss::channels] = std::move(jsonChannels);
-
212  return result;
-
213 }
-
214 
-
215 } // namespace ripple
+
205  for (auto const& item : visitData.items)
+
206  addChannel(jsonChannels, *item);
+
207 
+
208  context.loadType = Resource::feeMediumBurdenRPC;
+
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
@@ -303,16 +301,13 @@ $(function() {
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::VisitData::items
std::vector< RPCTrustLine > items
Definition: AccountLines.cpp:35
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::VisitData
Definition: AccountLines.cpp:33
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
-
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::sfSettleDelay
const SF_UINT32 sfSettleDelay
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
@@ -324,6 +319,7 @@ $(function() {
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
@@ -341,7 +337,6 @@ $(function() {
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::accountFromString
Json::Value accountFromString(AccountID &result, std::string const &strIdent, bool bStrict)
Definition: RPCHelpers.cpp:86
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
diff --git a/AccountCurrenciesHandler_8cpp_source.html b/AccountCurrenciesHandler_8cpp_source.html index e0cdff7900..6f5bcb3c8d 100644 --- a/AccountCurrenciesHandler_8cpp_source.html +++ b/AccountCurrenciesHandler_8cpp_source.html @@ -117,45 +117,46 @@ $(function() {
46  params.isMember(jss::account) ? params[jss::account].asString()
47  : params[jss::ident].asString());
48 
-
49  bool const bStrict =
-
50  params.isMember(jss::strict) && params[jss::strict].asBool();
-
51 
-
52  // Get info on account.
-
53  AccountID accountID; // out param
-
54  if (auto jvAccepted = RPC::accountFromString(accountID, strIdent, bStrict))
-
55  return jvAccepted;
-
56 
-
57  if (!ledger->exists(keylet::account(accountID)))
-
58  return rpcError(rpcACT_NOT_FOUND);
-
59 
-
60  std::set<Currency> send, receive;
-
61  for (auto const& rspEntry : RPCTrustLine::getItems(accountID, *ledger))
-
62  {
-
63  STAmount const& saBalance = rspEntry.getBalance();
-
64 
-
65  if (saBalance < rspEntry.getLimit())
-
66  receive.insert(saBalance.getCurrency());
-
67  if ((-saBalance) < rspEntry.getLimitPeer())
-
68  send.insert(saBalance.getCurrency());
-
69  }
-
70 
-
71  send.erase(badCurrency());
-
72  receive.erase(badCurrency());
-
73 
-
74  Json::Value& sendCurrencies =
-
75  (result[jss::send_currencies] = Json::arrayValue);
-
76  for (auto const& c : send)
-
77  sendCurrencies.append(to_string(c));
-
78 
-
79  Json::Value& recvCurrencies =
-
80  (result[jss::receive_currencies] = Json::arrayValue);
-
81  for (auto const& c : receive)
-
82  recvCurrencies.append(to_string(c));
-
83 
-
84  return result;
-
85 }
-
86 
-
87 } // namespace ripple
+
49  // Get info on account.
+
50  auto id = parseBase58<AccountID>(strIdent);
+
51  if (!id)
+
52  {
+
53  RPC::inject_error(rpcACT_MALFORMED, result);
+
54  return result;
+
55  }
+
56  auto const accountID{std::move(id.value())};
+
57 
+
58  if (!ledger->exists(keylet::account(accountID)))
+
59  return rpcError(rpcACT_NOT_FOUND);
+
60 
+
61  std::set<Currency> send, receive;
+
62  for (auto const& rspEntry : RPCTrustLine::getItems(accountID, *ledger))
+
63  {
+
64  STAmount const& saBalance = rspEntry.getBalance();
+
65 
+
66  if (saBalance < rspEntry.getLimit())
+
67  receive.insert(saBalance.getCurrency());
+
68  if ((-saBalance) < rspEntry.getLimitPeer())
+
69  send.insert(saBalance.getCurrency());
+
70  }
+
71 
+
72  send.erase(badCurrency());
+
73  receive.erase(badCurrency());
+
74 
+
75  Json::Value& sendCurrencies =
+
76  (result[jss::send_currencies] = Json::arrayValue);
+
77  for (auto const& c : send)
+
78  sendCurrencies.append(to_string(c));
+
79 
+
80  Json::Value& recvCurrencies =
+
81  (result[jss::receive_currencies] = Json::arrayValue);
+
82  for (auto const& c : receive)
+
83  recvCurrencies.append(to_string(c));
+
84 
+
85  return result;
+
86 }
+
87 
+
88 } // namespace ripple
ripple::badCurrency
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
Definition: UintTypes.cpp:135
ripple::RPC::JsonContext
Definition: Context.h:53
@@ -166,7 +167,6 @@ $(function() {
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h: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:675
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
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
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
@@ -174,12 +174,13 @@ $(function() {
ripple::ReadView::exists
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
std::set::erase
T erase(T... args)
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::set::insert
T insert(T... args)
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::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
ripple::STAmount::getCurrency
Currency const & getCurrency() const
Definition: STAmount.h:353
+
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
std::set
STL class.
-
ripple::RPC::accountFromString
Json::Value accountFromString(AccountID &result, std::string const &strIdent, bool bStrict)
Definition: RPCHelpers.cpp:86
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::RPCTrustLine::getItems
static std::vector< RPCTrustLine > getItems(AccountID const &accountID, ReadView const &view)
Definition: TrustLine.cpp:120
diff --git a/AccountCurrencies__test_8cpp_source.html b/AccountCurrencies__test_8cpp_source.html index a553b65b50..681fff3d6b 100644 --- a/AccountCurrencies__test_8cpp_source.html +++ b/AccountCurrencies__test_8cpp_source.html @@ -127,140 +127,151 @@ $(function() {
56  result[jss::error_message] == "Missing field 'account'.");
57  }
58 
-
59  { // strict mode, invalid bitcoin token
+
59  {
60  Json::Value params;
61  params[jss::account] =
62  "llIIOO"; // these are invalid in bitcoin alphabet
-
63  params[jss::strict] = true;
-
64  auto const result = env.rpc(
-
65  "json",
-
66  "account_currencies",
-
67  boost::lexical_cast<std::string>(params))[jss::result];
-
68  BEAST_EXPECT(result[jss::error] == "actMalformed");
-
69  BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
-
70  }
-
71 
-
72  { // ask for nonexistent account
+
63  auto const result = env.rpc(
+
64  "json",
+
65  "account_currencies",
+
66  boost::lexical_cast<std::string>(params))[jss::result];
+
67  BEAST_EXPECT(result[jss::error] == "actMalformed");
+
68  BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
+
69  }
+
70 
+
71  {
+
72  // Cannot use a seed as account
73  Json::Value params;
-
74  params[jss::account] = Account{"bob"}.human();
+
74  params[jss::account] = "Bob";
75  auto const result = env.rpc(
76  "json",
77  "account_currencies",
78  boost::lexical_cast<std::string>(params))[jss::result];
-
79  BEAST_EXPECT(result[jss::error] == "actNotFound");
-
80  BEAST_EXPECT(result[jss::error_message] == "Account not found.");
+
79  BEAST_EXPECT(result[jss::error] == "actMalformed");
+
80  BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
81  }
-
82  }
-
83 
-
84  void
-
85  testBasic()
-
86  {
-
87  testcase("Basic request for account_currencies");
-
88 
-
89  using namespace test::jtx;
-
90  Env env{*this};
-
91 
-
92  auto const alice = Account{"alice"};
-
93  auto const gw = Account{"gateway"};
-
94  env.fund(XRP(10000), alice, gw);
-
95  char currencySuffix{'A'};
-
96  std::vector<std::optional<IOU>> gwCurrencies(26); // A - Z
-
97  std::generate(gwCurrencies.begin(), gwCurrencies.end(), [&]() {
-
98  auto gwc = gw[std::string("US") + currencySuffix++];
-
99  env(trust(alice, gwc(100)));
-
100  return gwc;
-
101  });
-
102  env.close();
-
103 
-
104  Json::Value params;
-
105  params[jss::account] = alice.human();
-
106  auto result = env.rpc(
-
107  "json",
-
108  "account_currencies",
-
109  boost::lexical_cast<std::string>(params))[jss::result];
-
110 
-
111  auto arrayCheck =
-
112  [&result](
-
113  Json::StaticString const& fld,
-
114  std::vector<std::optional<IOU>> const& expected) -> bool {
-
115  bool stat = result.isMember(fld) && result[fld].isArray() &&
-
116  result[fld].size() == expected.size();
-
117  for (size_t i = 0; stat && i < expected.size(); ++i)
-
118  {
-
119  stat &=
-
120  (to_string(expected[i].value().currency) ==
-
121  result[fld][i].asString());
-
122  }
-
123  return stat;
-
124  };
-
125 
-
126  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
-
127  BEAST_EXPECT(arrayCheck(jss::send_currencies, {}));
-
128 
-
129  // now form a payment for each currency
-
130  for (auto const& c : gwCurrencies)
-
131  env(pay(gw, alice, c.value()(50)));
-
132 
-
133  // send_currencies should be populated now
-
134  result = env.rpc(
-
135  "json",
-
136  "account_currencies",
-
137  boost::lexical_cast<std::string>(params))[jss::result];
-
138  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
-
139  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
-
140 
-
141  // freeze the USD trust line and verify that the receive currencies
-
142  // does not change
-
143  env(trust(alice, gw["USD"](100), tfSetFreeze));
-
144  result = env.rpc("account_lines", alice.human());
-
145  for (auto const& l : result[jss::lines])
-
146  BEAST_EXPECT(
-
147  l[jss::freeze].asBool() == (l[jss::currency] == "USD"));
-
148  result = env.rpc(
-
149  "json",
-
150  "account_currencies",
-
151  boost::lexical_cast<std::string>(params))[jss::result];
-
152  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
-
153  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
-
154  // clear the freeze
-
155  env(trust(alice, gw["USD"](100), tfClearFreeze));
-
156 
-
157  // make a payment that exhausts the trustline from alice to gw for USA
-
158  env(pay(gw, alice, gw["USA"](50)));
-
159  // USA should now be missing from receive_currencies
-
160  result = env.rpc(
-
161  "json",
-
162  "account_currencies",
-
163  boost::lexical_cast<std::string>(params))[jss::result];
-
164  decltype(gwCurrencies) gwCurrenciesNoUSA(
-
165  gwCurrencies.begin() + 1, gwCurrencies.end());
-
166  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrenciesNoUSA));
-
167  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
-
168 
-
169  // add trust from gw to alice and then exhaust that trust line
-
170  // so that send_currencies for alice will now omit USA
-
171  env(trust(gw, alice["USA"](100)));
-
172  env(pay(alice, gw, alice["USA"](200)));
-
173  result = env.rpc(
-
174  "json",
-
175  "account_currencies",
-
176  boost::lexical_cast<std::string>(params))[jss::result];
-
177  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
-
178  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrenciesNoUSA));
-
179  }
-
180 
-
181 public:
-
182  void
-
183  run() override
-
184  {
-
185  testBadInput();
-
186  testBasic();
-
187  }
-
188 };
-
189 
-
190 BEAST_DEFINE_TESTSUITE(AccountCurrencies, app, ripple);
+
82 
+
83  { // ask for nonexistent account
+
84  Json::Value params;
+
85  params[jss::account] = Account{"bob"}.human();
+
86  auto const result = env.rpc(
+
87  "json",
+
88  "account_currencies",
+
89  boost::lexical_cast<std::string>(params))[jss::result];
+
90  BEAST_EXPECT(result[jss::error] == "actNotFound");
+
91  BEAST_EXPECT(result[jss::error_message] == "Account not found.");
+
92  }
+
93  }
+
94 
+
95  void
+
96  testBasic()
+
97  {
+
98  testcase("Basic request for account_currencies");
+
99 
+
100  using namespace test::jtx;
+
101  Env env{*this};
+
102 
+
103  auto const alice = Account{"alice"};
+
104  auto const gw = Account{"gateway"};
+
105  env.fund(XRP(10000), alice, gw);
+
106  char currencySuffix{'A'};
+
107  std::vector<std::optional<IOU>> gwCurrencies(26); // A - Z
+
108  std::generate(gwCurrencies.begin(), gwCurrencies.end(), [&]() {
+
109  auto gwc = gw[std::string("US") + currencySuffix++];
+
110  env(trust(alice, gwc(100)));
+
111  return gwc;
+
112  });
+
113  env.close();
+
114 
+
115  Json::Value params;
+
116  params[jss::account] = alice.human();
+
117  auto result = env.rpc(
+
118  "json",
+
119  "account_currencies",
+
120  boost::lexical_cast<std::string>(params))[jss::result];
+
121 
+
122  auto arrayCheck =
+
123  [&result](
+
124  Json::StaticString const& fld,
+
125  std::vector<std::optional<IOU>> const& expected) -> bool {
+
126  bool stat = result.isMember(fld) && result[fld].isArray() &&
+
127  result[fld].size() == expected.size();
+
128  for (size_t i = 0; stat && i < expected.size(); ++i)
+
129  {
+
130  stat &=
+
131  (to_string(expected[i].value().currency) ==
+
132  result[fld][i].asString());
+
133  }
+
134  return stat;
+
135  };
+
136 
+
137  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
+
138  BEAST_EXPECT(arrayCheck(jss::send_currencies, {}));
+
139 
+
140  // now form a payment for each currency
+
141  for (auto const& c : gwCurrencies)
+
142  env(pay(gw, alice, c.value()(50)));
+
143 
+
144  // send_currencies should be populated now
+
145  result = env.rpc(
+
146  "json",
+
147  "account_currencies",
+
148  boost::lexical_cast<std::string>(params))[jss::result];
+
149  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
+
150  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
+
151 
+
152  // freeze the USD trust line and verify that the receive currencies
+
153  // does not change
+
154  env(trust(alice, gw["USD"](100), tfSetFreeze));
+
155  result = env.rpc("account_lines", alice.human());
+
156  for (auto const& l : result[jss::lines])
+
157  BEAST_EXPECT(
+
158  l[jss::freeze].asBool() == (l[jss::currency] == "USD"));
+
159  result = env.rpc(
+
160  "json",
+
161  "account_currencies",
+
162  boost::lexical_cast<std::string>(params))[jss::result];
+
163  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
+
164  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
+
165  // clear the freeze
+
166  env(trust(alice, gw["USD"](100), tfClearFreeze));
+
167 
+
168  // make a payment that exhausts the trustline from alice to gw for USA
+
169  env(pay(gw, alice, gw["USA"](50)));
+
170  // USA should now be missing from receive_currencies
+
171  result = env.rpc(
+
172  "json",
+
173  "account_currencies",
+
174  boost::lexical_cast<std::string>(params))[jss::result];
+
175  decltype(gwCurrencies) gwCurrenciesNoUSA(
+
176  gwCurrencies.begin() + 1, gwCurrencies.end());
+
177  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrenciesNoUSA));
+
178  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
+
179 
+
180  // add trust from gw to alice and then exhaust that trust line
+
181  // so that send_currencies for alice will now omit USA
+
182  env(trust(gw, alice["USA"](100)));
+
183  env(pay(alice, gw, alice["USA"](200)));
+
184  result = env.rpc(
+
185  "json",
+
186  "account_currencies",
+
187  boost::lexical_cast<std::string>(params))[jss::result];
+
188  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
+
189  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrenciesNoUSA));
+
190  }
191 
-
192 } // namespace ripple
+
192 public:
+
193  void
+
194  run() override
+
195  {
+
196  testBadInput();
+
197  testBasic();
+
198  }
+
199 };
+
200 
+
201 BEAST_DEFINE_TESTSUITE(AccountCurrencies, app, ripple);
+
202 
+
203 } // namespace ripple
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
ripple::AccountCurrencies_test::testBadInput
void testBadInput()
Definition: AccountCurrencies_test.cpp:29
@@ -269,14 +280,14 @@ $(function() {
ripple::AccountCurrencies_test
Definition: AccountCurrencies_test.cpp:26
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
-
ripple::AccountCurrencies_test::run
void run() override
Definition: AccountCurrencies_test.cpp:183
+
ripple::AccountCurrencies_test::run
void run() override
Definition: AccountCurrencies_test.cpp:194
ripple::tfSetFreeze
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:111
std::vector::begin
T begin(T... args)
Json::StaticString
Lightweight wrapper to tag static string.
Definition: json_value.h:60
ripple::tfClearFreeze
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:112
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
std::vector::end
T end(T... args)
-
ripple::AccountCurrencies_test::testBasic
void testBasic()
Definition: AccountCurrencies_test.cpp:85
+
ripple::AccountCurrencies_test::testBasic
void testBasic()
Definition: AccountCurrencies_test.cpp:96
Json::Value
Represents a JSON value.
Definition: json_value.h:145