rippled
Loading...
Searching...
No Matches
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 <xrpl/beast/unit_test.h>
22#include <xrpl/protocol/jss.h>
23
24namespace ripple {
25
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::account] = Account{"bob"}.human();
43 params[jss::ledger_hash] = 1;
44 auto const result = env.rpc(
45 "json",
46 "account_currencies",
47 boost::lexical_cast<std::string>(params))[jss::result];
48 BEAST_EXPECT(result[jss::error] == "invalidParams");
49 BEAST_EXPECT(result[jss::error_message] == "ledgerHashNotString");
50 }
51
52 { // missing account field
53 auto const result =
54 env.rpc("json", "account_currencies", "{}")[jss::result];
55 BEAST_EXPECT(result[jss::error] == "invalidParams");
56 BEAST_EXPECT(
57 result[jss::error_message] == "Missing field 'account'.");
58 }
59
60 {
61 // test account non-string
62 auto testInvalidAccountParam = [&](auto const& param) {
63 Json::Value params;
64 params[jss::account] = param;
65 auto jrr = env.rpc(
66 "json",
67 "account_currencies",
68 to_string(params))[jss::result];
69 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
70 BEAST_EXPECT(
71 jrr[jss::error_message] == "Invalid field 'account'.");
72 };
73
74 testInvalidAccountParam(1);
75 testInvalidAccountParam(1.1);
76 testInvalidAccountParam(true);
77 testInvalidAccountParam(Json::Value(Json::nullValue));
78 testInvalidAccountParam(Json::Value(Json::objectValue));
79 testInvalidAccountParam(Json::Value(Json::arrayValue));
80 }
81
82 {
83 // test ident non-string
84 auto testInvalidIdentParam = [&](auto const& param) {
85 Json::Value params;
86 params[jss::ident] = param;
87 auto jrr = env.rpc(
88 "json",
89 "account_currencies",
90 to_string(params))[jss::result];
91 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
92 BEAST_EXPECT(
93 jrr[jss::error_message] == "Invalid field 'ident'.");
94 };
95
96 testInvalidIdentParam(1);
97 testInvalidIdentParam(1.1);
98 testInvalidIdentParam(true);
99 testInvalidIdentParam(Json::Value(Json::nullValue));
100 testInvalidIdentParam(Json::Value(Json::objectValue));
101 testInvalidIdentParam(Json::Value(Json::arrayValue));
102 }
103
104 {
105 Json::Value params;
106 params[jss::account] =
107 "llIIOO"; // these are invalid in bitcoin alphabet
108 auto const result = env.rpc(
109 "json",
110 "account_currencies",
111 boost::lexical_cast<std::string>(params))[jss::result];
112 BEAST_EXPECT(result[jss::error] == "actMalformed");
113 BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
114 }
115
116 {
117 // Cannot use a seed as account
118 Json::Value params;
119 params[jss::account] = "Bob";
120 auto const result = env.rpc(
121 "json",
122 "account_currencies",
123 boost::lexical_cast<std::string>(params))[jss::result];
124 BEAST_EXPECT(result[jss::error] == "actMalformed");
125 BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
126 }
127
128 { // ask for nonexistent account
129 Json::Value params;
130 params[jss::account] = Account{"bob"}.human();
131 auto const result = env.rpc(
132 "json",
133 "account_currencies",
134 boost::lexical_cast<std::string>(params))[jss::result];
135 BEAST_EXPECT(result[jss::error] == "actNotFound");
136 BEAST_EXPECT(result[jss::error_message] == "Account not found.");
137 }
138 }
139
140 void
142 {
143 testcase("Basic request for account_currencies");
144
145 using namespace test::jtx;
146 Env env{*this};
147
148 auto const alice = Account{"alice"};
149 auto const gw = Account{"gateway"};
150 env.fund(XRP(10000), alice, gw);
151 char currencySuffix{'A'};
152 std::vector<std::optional<IOU>> gwCurrencies(26); // A - Z
153 std::generate(gwCurrencies.begin(), gwCurrencies.end(), [&]() {
154 auto gwc = gw[std::string("US") + currencySuffix++];
155 env(trust(alice, gwc(100)));
156 return gwc;
157 });
158 env.close();
159
160 Json::Value params;
161 params[jss::account] = alice.human();
162 auto result = env.rpc(
163 "json",
164 "account_currencies",
165 boost::lexical_cast<std::string>(params))[jss::result];
166
167 auto arrayCheck =
168 [&result](
169 Json::StaticString const& fld,
170 std::vector<std::optional<IOU>> const& expected) -> bool {
171 bool stat = result.isMember(fld) && result[fld].isArray() &&
172 result[fld].size() == expected.size();
173 for (size_t i = 0; stat && i < expected.size(); ++i)
174 {
175 stat &=
176 (to_string(expected[i].value().currency) ==
177 result[fld][i].asString());
178 }
179 return stat;
180 };
181
182 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
183 BEAST_EXPECT(arrayCheck(jss::send_currencies, {}));
184
185 // now form a payment for each currency
186 for (auto const& c : gwCurrencies)
187 env(pay(gw, alice, c.value()(50)));
188
189 // send_currencies should be populated now
190 result = env.rpc(
191 "json",
192 "account_currencies",
193 boost::lexical_cast<std::string>(params))[jss::result];
194 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
195 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
196
197 // freeze the USD trust line and verify that the receive currencies
198 // does not change
199 env(trust(alice, gw["USD"](100), tfSetFreeze));
200 result = env.rpc("account_lines", alice.human());
201 for (auto const& l : result[jss::lines])
202 BEAST_EXPECT(
203 l[jss::freeze].asBool() == (l[jss::currency] == "USD"));
204 result = env.rpc(
205 "json",
206 "account_currencies",
207 boost::lexical_cast<std::string>(params))[jss::result];
208 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
209 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
210 // clear the freeze
211 env(trust(alice, gw["USD"](100), tfClearFreeze));
212
213 // make a payment that exhausts the trustline from alice to gw for USA
214 env(pay(gw, alice, gw["USA"](50)));
215 // USA should now be missing from receive_currencies
216 result = env.rpc(
217 "json",
218 "account_currencies",
219 boost::lexical_cast<std::string>(params))[jss::result];
220 decltype(gwCurrencies) gwCurrenciesNoUSA(
221 gwCurrencies.begin() + 1, gwCurrencies.end());
222 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrenciesNoUSA));
223 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrencies));
224
225 // add trust from gw to alice and then exhaust that trust line
226 // so that send_currencies for alice will now omit USA
227 env(trust(gw, alice["USA"](100)));
228 env(pay(alice, gw, alice["USA"](200)));
229 result = env.rpc(
230 "json",
231 "account_currencies",
232 boost::lexical_cast<std::string>(params))[jss::result];
233 BEAST_EXPECT(arrayCheck(jss::receive_currencies, gwCurrencies));
234 BEAST_EXPECT(arrayCheck(jss::send_currencies, gwCurrenciesNoUSA));
235 }
236
237public:
238 void
239 run() override
240 {
241 testBadInput();
242 testBasic();
243 }
244};
245
246BEAST_DEFINE_TESTSUITE(AccountCurrencies, rpc, ripple);
247
248} // namespace ripple
T begin(T... args)
Lightweight wrapper to tag static string.
Definition: json_value.h:61
Represents a JSON value.
Definition: json_value.h:147
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:943
A testsuite class.
Definition: suite.h:53
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:153
void run() override
Runs the suite.
T end(T... args)
T generate(T... args)
@ nullValue
'null' value
Definition: json_value.h:36
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:116
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:115