rippled
AccountCurrencies_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2017 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/beast/unit_test.h>
21 #include <ripple/protocol/jss.h>
22 #include <test/jtx.h>
23 
24 namespace ripple {
25 
26 class AccountCurrencies_test : public beast::unit_test::suite
27 {
28  void
30  {
31  testcase("Bad input to account_currencies");
32 
33  using namespace test::jtx;
34  Env env{*this};
35 
36  auto const alice = Account{"alice"};
37  env.fund(XRP(10000), alice);
38  env.close();
39 
40  { // invalid ledger (hash)
41  Json::Value params;
42  params[jss::ledger_hash] = 1;
43  auto const result = env.rpc(
44  "json",
45  "account_currencies",
46  boost::lexical_cast<std::string>(params))[jss::result];
47  BEAST_EXPECT(result[jss::error] == "invalidParams");
48  BEAST_EXPECT(result[jss::error_message] == "ledgerHashNotString");
49  }
50 
51  { // missing account field
52  auto const result =
53  env.rpc("json", "account_currencies", "{}")[jss::result];
54  BEAST_EXPECT(result[jss::error] == "invalidParams");
55  BEAST_EXPECT(
56  result[jss::error_message] == "Missing field 'account'.");
57  }
58 
59  {
60  Json::Value params;
61  params[jss::account] =
62  "llIIOO"; // these are invalid in bitcoin alphabet
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] = "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] == "actMalformed");
80  BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
81  }
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
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 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
std::vector
STL class.
std::generate
T generate(T... args)
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: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:96
Json::Value
Represents a JSON value.
Definition: json_value.h:145