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