rippled
Loading...
Searching...
No Matches
AMMInfo_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2023 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#include <test/jtx/AMM.h>
22#include <test/jtx/AMMTest.h>
23
24#include <xrpl/protocol/jss.h>
25
26#include <unordered_map>
27
28namespace ripple {
29namespace test {
30
32{
33public:
34 void
36 {
37 testcase("Errors");
38
39 using namespace jtx;
40
41 Account const bogie("bogie");
42 enum TestAccount { None, Alice, Bogie };
43 auto accountId = [&](AMM const& ammAlice,
44 TestAccount v) -> std::optional<AccountID> {
45 if (v == Alice)
46 return ammAlice.ammAccount();
47 else if (v == Bogie)
48 return bogie;
49 else
50 return std::nullopt;
51 };
52
53 // Invalid tokens pair
54 testAMM([&](AMM& ammAlice, Env&) {
55 Account const gw("gw");
56 auto const USD = gw["USD"];
57 auto const jv =
58 ammAlice.ammRpcInfo({}, {}, USD.issue(), USD.issue());
59 BEAST_EXPECT(jv[jss::error_message] == "Account not found.");
60 });
61
62 // Invalid LP account id
63 testAMM([&](AMM& ammAlice, Env&) {
64 auto const jv = ammAlice.ammRpcInfo(bogie.id());
65 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
66 });
67
71 TestAccount,
72 bool>> const invalidParams = {
73 {xrpIssue(), std::nullopt, None, false},
74 {std::nullopt, USD.issue(), None, false},
75 {xrpIssue(), std::nullopt, Alice, false},
76 {std::nullopt, USD.issue(), Alice, false},
77 {xrpIssue(), USD.issue(), Alice, false},
78 {std::nullopt, std::nullopt, None, true}};
79
80 // Invalid parameters
81 testAMM([&](AMM& ammAlice, Env&) {
82 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
83 {
84 auto const jv = ammAlice.ammRpcInfo(
85 std::nullopt,
86 std::nullopt,
87 iss1,
88 iss2,
89 accountId(ammAlice, acct),
90 ignoreParams);
91 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
92 }
93 });
94
95 // Invalid parameters *and* invalid LP account, default API version
96 testAMM([&](AMM& ammAlice, Env&) {
97 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
98 {
99 auto const jv = ammAlice.ammRpcInfo(
100 bogie, //
101 std::nullopt,
102 iss1,
103 iss2,
104 accountId(ammAlice, acct),
105 ignoreParams);
106 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
107 }
108 });
109
110 // Invalid parameters *and* invalid LP account, API version 3
111 testAMM([&](AMM& ammAlice, Env&) {
112 for (auto const& [iss1, iss2, acct, ignoreParams] : invalidParams)
113 {
114 auto const jv = ammAlice.ammRpcInfo(
115 bogie, //
116 std::nullopt,
117 iss1,
118 iss2,
119 accountId(ammAlice, acct),
120 ignoreParams,
121 3);
122 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
123 }
124 });
125
126 // Invalid AMM account id
127 testAMM([&](AMM& ammAlice, Env&) {
128 auto const jv = ammAlice.ammRpcInfo(
129 std::nullopt,
130 std::nullopt,
131 std::nullopt,
132 std::nullopt,
133 bogie.id());
134 BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
135 });
136
140 TestAccount,
141 bool>> const invalidParamsBadAccount = {
142 {xrpIssue(), std::nullopt, None, false},
143 {std::nullopt, USD.issue(), None, false},
144 {xrpIssue(), std::nullopt, Bogie, false},
145 {std::nullopt, USD.issue(), Bogie, false},
146 {xrpIssue(), USD.issue(), Bogie, false},
147 {std::nullopt, std::nullopt, None, true}};
148
149 // Invalid parameters *and* invalid AMM account, default API version
150 testAMM([&](AMM& ammAlice, Env&) {
151 for (auto const& [iss1, iss2, acct, ignoreParams] :
152 invalidParamsBadAccount)
153 {
154 auto const jv = ammAlice.ammRpcInfo(
155 std::nullopt,
156 std::nullopt,
157 iss1,
158 iss2,
159 accountId(ammAlice, acct),
160 ignoreParams);
161 BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
162 }
163 });
164
165 // Invalid parameters *and* invalid AMM account, API version 3
166 testAMM([&](AMM& ammAlice, Env&) {
167 for (auto const& [iss1, iss2, acct, ignoreParams] :
168 invalidParamsBadAccount)
169 {
170 auto const jv = ammAlice.ammRpcInfo(
171 std::nullopt,
172 std::nullopt,
173 iss1,
174 iss2,
175 accountId(ammAlice, acct),
176 ignoreParams,
177 3);
178 BEAST_EXPECT(
179 jv[jss::error_message] ==
180 (acct == Bogie ? std::string("Account malformed.")
181 : std::string("Invalid parameters.")));
182 }
183 });
184 }
185
186 void
188 {
189 testcase("RPC simple");
190
191 using namespace jtx;
192 testAMM([&](AMM& ammAlice, Env&) {
193 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
194 XRP(10000), USD(10000), IOUAmount{10000000, 0}));
195 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
196 XRP(10000),
197 USD(10000),
198 IOUAmount{10000000, 0},
199 std::nullopt,
200 std::nullopt,
201 ammAlice.ammAccount()));
202 });
203 }
204
205 void
207 {
208 testcase("Vote and Bid");
209
210 using namespace jtx;
211 testAMM([&](AMM& ammAlice, Env& env) {
212 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
213 XRP(10000), USD(10000), IOUAmount{10000000, 0}));
215 votes.insert({alice.human(), 0});
216 for (int i = 0; i < 7; ++i)
217 {
219 votes.insert({a.human(), 50 * (i + 1)});
220 fund(env, gw, {a}, {USD(10000)}, Fund::Acct);
221 ammAlice.deposit(a, 10000000);
222 ammAlice.vote(a, 50 * (i + 1));
223 }
224 BEAST_EXPECT(ammAlice.expectTradingFee(175));
225 Account ed("ed");
226 Account bill("bill");
227 env.fund(XRP(1000), bob, ed, bill);
228 env(ammAlice.bid(
229 {.bidMin = 100, .authAccounts = {carol, bob, ed, bill}}));
230 BEAST_EXPECT(ammAlice.expectAmmRpcInfo(
231 XRP(80000),
232 USD(80000),
233 IOUAmount{79994400},
234 std::nullopt,
235 std::nullopt,
236 ammAlice.ammAccount()));
237 for (auto i = 0; i < 2; ++i)
238 {
239 std::unordered_set<std::string> authAccounts = {
240 carol.human(), bob.human(), ed.human(), bill.human()};
241 auto const ammInfo = i ? ammAlice.ammRpcInfo()
242 : ammAlice.ammRpcInfo(
243 std::nullopt,
244 std::nullopt,
245 std::nullopt,
246 std::nullopt,
247 ammAlice.ammAccount());
248 auto const& amm = ammInfo[jss::amm];
249 try
250 {
251 // votes
252 auto const voteSlots = amm[jss::vote_slots];
253 auto votesCopy = votes;
254 for (std::uint8_t i = 0; i < 8; ++i)
255 {
256 if (!BEAST_EXPECT(
257 votes[voteSlots[i][jss::account].asString()] ==
258 voteSlots[i][jss::trading_fee].asUInt() &&
259 voteSlots[i][jss::vote_weight].asUInt() ==
260 12500))
261 return;
262 votes.erase(voteSlots[i][jss::account].asString());
263 }
264 if (!BEAST_EXPECT(votes.empty()))
265 return;
266 votes = votesCopy;
267
268 // bid
269 auto const auctionSlot = amm[jss::auction_slot];
270 for (std::uint8_t i = 0; i < 4; ++i)
271 {
272 if (!BEAST_EXPECT(authAccounts.contains(
273 auctionSlot[jss::auth_accounts][i][jss::account]
274 .asString())))
275 return;
276 authAccounts.erase(
277 auctionSlot[jss::auth_accounts][i][jss::account]
278 .asString());
279 }
280 if (!BEAST_EXPECT(authAccounts.empty()))
281 return;
282 BEAST_EXPECT(
283 auctionSlot[jss::account].asString() == alice.human() &&
284 auctionSlot[jss::discounted_fee].asUInt() == 17 &&
285 auctionSlot[jss::price][jss::value].asString() ==
286 "5600" &&
287 auctionSlot[jss::price][jss::currency].asString() ==
288 to_string(ammAlice.lptIssue().currency) &&
289 auctionSlot[jss::price][jss::issuer].asString() ==
290 to_string(ammAlice.lptIssue().account));
291 }
292 catch (std::exception const& e)
293 {
294 fail(e.what(), __FILE__, __LINE__);
295 }
296 }
297 });
298 }
299
300 void
302 {
303 using namespace jtx;
304 testAMM([&](AMM& ammAlice, Env& env) {
305 env(fset(gw, asfGlobalFreeze));
306 env.close();
307 auto test = [&](bool freeze) {
308 auto const info = ammAlice.ammRpcInfo();
309 BEAST_EXPECT(
310 info[jss::amm][jss::asset2_frozen].asBool() == freeze);
311 };
312 test(true);
313 env(fclear(gw, asfGlobalFreeze));
314 env.close();
315 test(false);
316 });
317 }
318
319 void
321 {
322 using namespace jtx;
323 testcase("Invalid amm field");
324
325 testAMM([&](AMM& amm, Env&) {
326 auto const resp = amm.ammRpcInfo(
327 std::nullopt,
328 jss::validated.c_str(),
329 std::nullopt,
330 std::nullopt,
331 gw);
332 BEAST_EXPECT(
333 resp.isMember("error") && resp["error"] == "actNotFound");
334 });
335 }
336
337 void
338 run() override
339 {
340 testErrors();
341 testSimpleRpc();
342 testVoteAndBid();
343 testFreeze();
344 testInvalidAmmField();
345 }
346};
347
348BEAST_DEFINE_TESTSUITE(AMMInfo, app, ripple);
349
350} // namespace test
351} // namespace ripple
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition: suite.h:533
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:46
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
void run() override
Runs the suite.
jtx::Account const alice
Definition: AMMTest.h:68
jtx::Account const gw
Definition: AMMTest.h:66
jtx::Account const bob
Definition: AMMTest.h:69
jtx::Account const carol
Definition: AMMTest.h:67
void testAMM(std::function< void(jtx::AMM &, jtx::Env &)> &&cb, std::optional< std::pair< STAmount, STAmount > > const &pool=std::nullopt, std::uint16_t tfee=0, std::optional< jtx::ter > const &ter=std::nullopt, std::vector< FeatureBitset > const &features={supported_amendments()})
testAMM() funds 30,000XRP and 30,000IOU for each non-XRP asset to Alice and Carol
Definition: AMMTest.cpp:102
Convenience class to test AMM functionality.
Definition: AMM.h:124
Json::Value ammRpcInfo(std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledgerIndex=std::nullopt, std::optional< Issue > issue1=std::nullopt, std::optional< Issue > issue2=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt, bool ignoreParams=false, unsigned apiVersion=RPC::apiInvalidVersion) const
Send amm_info RPC command.
Definition: AMM.cpp:161
void vote(std::optional< Account > const &account, std::uint32_t feeVal, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< jtx::seq > const &seq=std::nullopt, std::optional< std::pair< Issue, Issue > > const &assets=std::nullopt, std::optional< ter > const &ter=std::nullopt)
Definition: AMM.cpp:637
AccountID const & ammAccount() const
Definition: AMM.h:325
bool expectTradingFee(std::uint16_t fee) const
Definition: AMM.cpp:312
Issue lptIssue() const
Definition: AMM.h:331
IOUAmount deposit(std::optional< Account > const &account, LPToken tokens, std::optional< STAmount > const &asset1InDetails=std::nullopt, std::optional< std::uint32_t > const &flags=std::nullopt, std::optional< ter > const &ter=std::nullopt)
Definition: AMM.cpp:411
Json::Value bid(BidArg const &arg)
Definition: AMM.cpp:664
bool expectAmmRpcInfo(STAmount const &asset1, STAmount const &asset2, IOUAmount const &balance, std::optional< AccountID > const &account=std::nullopt, std::optional< std::string > const &ledger_index=std::nullopt, std::optional< AccountID > const &ammAccount=std::nullopt) const
Definition: AMM.cpp:328
Immutable cryptographic account descriptor.
Definition: Account.h:39
AccountID id() const
Returns the Account ID.
Definition: Account.h:107
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:114
A transaction testing environment.
Definition: Env.h:121
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
T contains(T... args)
T empty(T... args)
T erase(T... args)
T insert(T... args)
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition: flags.h:41
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:29
void fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition: AMMTest.cpp:36
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
constexpr std::uint32_t asfGlobalFreeze
Definition: TxFlags.h:82
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
T to_string(T... args)
T what(T... args)