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 <test/jtx.h>
21 #include <ripple/beast/unit_test.h>
22 #include <ripple/protocol/jss.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 ("json", "account_currencies",
44  boost::lexical_cast<std::string>(params)) [jss::result];
45  BEAST_EXPECT (result[jss::error] == "invalidParams");
46  BEAST_EXPECT (result[jss::error_message] ==
47  "ledgerHashNotString");
48  }
49 
50  { // missing account field
51  auto const result =
52  env.rpc ("json", "account_currencies", "{}") [jss::result];
53  BEAST_EXPECT (result[jss::error] == "invalidParams");
54  BEAST_EXPECT (result[jss::error_message] ==
55  "Missing field 'account'.");
56  }
57 
58  { // strict mode, invalid bitcoin token
59  Json::Value params;
60  params[jss::account] = "llIIOO"; //these are invalid in bitcoin alphabet
61  params[jss::strict] = true;
62  auto const result = env.rpc ("json", "account_currencies",
63  boost::lexical_cast<std::string>(params)) [jss::result];
64  BEAST_EXPECT (result[jss::error] == "actMalformed");
65  BEAST_EXPECT (result[jss::error_message] ==
66  "Account malformed.");
67  }
68 
69  { // strict mode, using properly formatted bitcoin token
70  Json::Value params;
71  params[jss::account] = base58EncodeTokenBitcoin (
72  TokenType::AccountID, alice.id().data(), alice.id().size());
73  params[jss::strict] = true;
74  auto const result = env.rpc ("json", "account_currencies",
75  boost::lexical_cast<std::string>(params)) [jss::result];
76  BEAST_EXPECT (result[jss::error] == "actBitcoin");
77  BEAST_EXPECT (result[jss::error_message] ==
78  "Account is bitcoin address.");
79  }
80 
81  { // ask for nonexistent account
82  Json::Value params;
83  params[jss::account] = Account{"bob"}.human();
84  auto const result = env.rpc ("json", "account_currencies",
85  boost::lexical_cast<std::string>(params)) [jss::result];
86  BEAST_EXPECT (result[jss::error] == "actNotFound");
87  BEAST_EXPECT (result[jss::error_message] ==
88  "Account not found.");
89  }
90  }
91 
92  void
94  {
95  testcase ("Basic request for account_currencies");
96 
97  using namespace test::jtx;
98  Env env {*this};
99 
100  auto const alice = Account {"alice"};
101  auto const gw = Account {"gateway"};
102  env.fund (XRP(10000), alice, gw);
103  char currencySuffix {'A'};
104  std::vector<boost::optional<IOU>> gwCurrencies (26); // A - Z
105  std::generate (gwCurrencies.begin(), gwCurrencies.end(),
106  [&]()
107  {
108  auto gwc = gw[std::string("US") + currencySuffix++];
109  env (trust (alice, gwc (100)));
110  return gwc;
111  });
112  env.close ();
113 
114  Json::Value params;
115  params[jss::account] = alice.human();
116  auto result = env.rpc ("json", "account_currencies",
117  boost::lexical_cast<std::string>(params)) [jss::result];
118 
119  auto arrayCheck =
120  [&result] (
121  Json::StaticString const& fld,
122  std::vector<boost::optional<IOU>> const& expected) -> bool
123  {
124  bool stat =
125  result.isMember (fld) &&
126  result[fld].isArray() &&
127  result[fld].size() == expected.size();
128  for (size_t i = 0; stat && i < expected.size(); ++i)
129  {
130  Currency foo;
131  stat &= (
132  to_string(expected[i].value().currency) ==
133  result[fld][i].asString()
134  );
135  }
136  return stat;
137  };
138 
139  BEAST_EXPECT (arrayCheck (jss::receive_currencies, gwCurrencies));
140  BEAST_EXPECT (arrayCheck (jss::send_currencies, {}));
141 
142  // now form a payment for each currency
143  for (auto const& c : gwCurrencies)
144  env (pay (gw, alice, c.value()(50)));
145 
146  // send_currencies should be populated now
147  result = env.rpc ("json", "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 ("json", "account_currencies",
160  boost::lexical_cast<std::string>(params)) [jss::result];
161  BEAST_EXPECT (arrayCheck (jss::receive_currencies, gwCurrencies));
162  BEAST_EXPECT (arrayCheck (jss::send_currencies, gwCurrencies));
163  // clear the freeze
164  env(trust(alice, gw["USD"](100), tfClearFreeze));
165 
166  // make a payment that exhausts the trustline from alice to gw for USA
167  env (pay (gw, alice, gw["USA"](50)));
168  // USA should now be missing from receive_currencies
169  result = env.rpc ("json", "account_currencies",
170  boost::lexical_cast<std::string>(params)) [jss::result];
171  decltype(gwCurrencies) gwCurrenciesNoUSA (gwCurrencies.begin() + 1,
172  gwCurrencies.end());
173  BEAST_EXPECT (arrayCheck (jss::receive_currencies, gwCurrenciesNoUSA));
174  BEAST_EXPECT (arrayCheck (jss::send_currencies, gwCurrencies));
175 
176  // add trust from gw to alice and then exhaust that trust line
177  // so that send_currencies for alice will now omit USA
178  env (trust (gw, alice["USA"] (100)));
179  env (pay (alice, gw, alice["USA"](200)));
180  result = env.rpc ("json", "account_currencies",
181  boost::lexical_cast<std::string>(params)) [jss::result];
182  BEAST_EXPECT (arrayCheck (jss::receive_currencies, gwCurrencies));
183  BEAST_EXPECT (arrayCheck (jss::send_currencies, gwCurrenciesNoUSA));
184  }
185 
186 public:
187  void run () override
188  {
189  testBadInput ();
190  testBasic ();
191  }
192 };
193 
194 BEAST_DEFINE_TESTSUITE(AccountCurrencies,app,ripple);
195 
196 } // ripple
197 
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:93
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:961
ripple::tfSetFreeze
const std::uint32_t tfSetFreeze
Definition: TxFlags.h:92
ripple::base58EncodeTokenBitcoin
std::string base58EncodeTokenBitcoin(TokenType type, void const *token, std::size_t size)
Definition: tokens.cpp:189
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:187
std::vector::begin
T begin(T... args)
Json::StaticString
Lightweight wrapper to tag static string.
Definition: json_value.h:62
std::vector::end
T end(T... args)
ripple::AccountCurrencies_test::testBasic
void testBasic()
Definition: AccountCurrencies_test.cpp:93
Json::Value
Represents a JSON value.
Definition: json_value.h:141