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