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