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  { // strict mode, invalid bitcoin token
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  { // strict mode, using properly formatted bitcoin token
73  Json::Value params;
74  params[jss::account] = base58EncodeTokenBitcoin(
75  TokenType::AccountID, alice.id().data(), alice.id().size());
76  params[jss::strict] = true;
77  auto const result = env.rpc(
78  "json",
79  "account_currencies",
80  boost::lexical_cast<std::string>(params))[jss::result];
81  BEAST_EXPECT(result[jss::error] == "actBitcoin");
82  BEAST_EXPECT(
83  result[jss::error_message] == "Account is bitcoin address.");
84  }
85 
86  { // ask for nonexistent account
87  Json::Value params;
88  params[jss::account] = Account{"bob"}.human();
89  auto const result = env.rpc(
90  "json",
91  "account_currencies",
92  boost::lexical_cast<std::string>(params))[jss::result];
93  BEAST_EXPECT(result[jss::error] == "actNotFound");
94  BEAST_EXPECT(result[jss::error_message] == "Account not found.");
95  }
96  }
97 
98  void
100  {
101  testcase("Basic request for account_currencies");
102 
103  using namespace test::jtx;
104  Env env{*this};
105 
106  auto const alice = Account{"alice"};
107  auto const gw = Account{"gateway"};
108  env.fund(XRP(10000), alice, gw);
109  char currencySuffix{'A'};
110  std::vector<boost::optional<IOU>> gwCurrencies(26); // A - Z
111  std::generate(gwCurrencies.begin(), gwCurrencies.end(), [&]() {
112  auto gwc = gw[std::string("US") + currencySuffix++];
113  env(trust(alice, gwc(100)));
114  return gwc;
115  });
116  env.close();
117 
118  Json::Value params;
119  params[jss::account] = alice.human();
120  auto result = env.rpc(
121  "json",
122  "account_currencies",
123  boost::lexical_cast<std::string>(params))[jss::result];
124 
125  auto arrayCheck =
126  [&result](
127  Json::StaticString const& fld,
128  std::vector<boost::optional<IOU>> const& expected) -> bool {
129  bool stat = result.isMember(fld) && result[fld].isArray() &&
130  result[fld].size() == expected.size();
131  for (size_t i = 0; stat && i < expected.size(); ++i)
132  {
133  Currency foo;
134  stat &=
135  (to_string(expected[i].value().currency) ==
136  result[fld][i].asString());
137  }
138  return stat;
139  };
140 
141  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
142  BEAST_EXPECT(arrayCheck(jss::send_currencies, {}));
143 
144  // now form a payment for each currency
145  for (auto const& c : gwCurrencies)
146  env(pay(gw, alice, c.value()(50)));
147 
148  // send_currencies should be populated now
149  result = env.rpc(
150  "json",
151  "account_currencies",
152  boost::lexical_cast<std::string>(params))[jss::result];
153  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
154  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
155 
156  // freeze the USD trust line and verify that the receive currencies
157  // does not change
158  env(trust(alice, gw["USD"](100), tfSetFreeze));
159  result = env.rpc("account_lines", alice.human());
160  for (auto const& l : result[jss::lines])
161  BEAST_EXPECT(
162  l[jss::freeze].asBool() == (l[jss::currency] == "USD"));
163  result = env.rpc(
164  "json",
165  "account_currencies",
166  boost::lexical_cast<std::string>(params))[jss::result];
167  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
168  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
169  // clear the freeze
170  env(trust(alice, gw["USD"](100), tfClearFreeze));
171 
172  // make a payment that exhausts the trustline from alice to gw for USA
173  env(pay(gw, alice, gw["USA"](50)));
174  // USA should now be missing from receive_currencies
175  result = env.rpc(
176  "json",
177  "account_currencies",
178  boost::lexical_cast<std::string>(params))[jss::result];
179  decltype(gwCurrencies) gwCurrenciesNoUSA(
180  gwCurrencies.begin() + 1, gwCurrencies.end());
181  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrenciesNoUSA));
182  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
183 
184  // add trust from gw to alice and then exhaust that trust line
185  // so that send_currencies for alice will now omit USA
186  env(trust(gw, alice["USA"](100)));
187  env(pay(alice, gw, alice["USA"](200)));
188  result = env.rpc(
189  "json",
190  "account_currencies",
191  boost::lexical_cast<std::string>(params))[jss::result];
192  BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
193  BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrenciesNoUSA));
194  }
195 
196 public:
197  void
198  run() override
199  {
200  testBadInput();
201  testBasic();
202  }
203 };
204 
205 BEAST_DEFINE_TESTSUITE(AccountCurrencies, app, ripple);
206 
207 } // 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::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
ripple::tfClearFreeze
const std::uint32_t tfClearFreeze
Definition: TxFlags.h:95
ripple::TokenType::AccountID
@ AccountID
ripple::base_uint< 160, detail::CurrencyTag >
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::tfSetFreeze
const std::uint32_t tfSetFreeze
Definition: TxFlags.h:94
ripple::base58EncodeTokenBitcoin
std::string base58EncodeTokenBitcoin(TokenType type, void const *token, std::size_t size)
Definition: tokens.cpp:188
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:198
std::vector::begin
T begin(T... args)
Json::StaticString
Lightweight wrapper to tag static string.
Definition: json_value.h:60
std::vector::end
T end(T... args)
ripple::AccountCurrencies_test::testBasic
void testBasic()
Definition: AccountCurrencies_test.cpp:99
Json::Value
Represents a JSON value.
Definition: json_value.h:145