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