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