rippled
AMMTest.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/AMMTest.h>
21 
22 #include <ripple/protocol/STParsedJSON.h>
23 #include <ripple/resource/Fees.h>
24 #include <ripple/rpc/RPCHandler.h>
25 #include <ripple/rpc/impl/RPCHelpers.h>
26 #include <test/jtx/AMM.h>
27 #include <test/jtx/Env.h>
28 #include <test/jtx/pay.h>
29 
30 namespace ripple {
31 namespace test {
32 namespace jtx {
33 
34 void
36  jtx::Env& env,
37  jtx::Account const& gw,
38  std::vector<jtx::Account> const& accounts,
39  std::vector<STAmount> const& amts,
40  Fund how)
41 {
42  fund(env, gw, accounts, XRP(30000), amts, how);
43 }
44 
45 void
47  jtx::Env& env,
48  std::vector<jtx::Account> const& accounts,
49  STAmount const& xrp,
50  std::vector<STAmount> const& amts,
51  Fund how)
52 {
53  for (auto const& account : accounts)
54  {
55  if (how == Fund::All || how == Fund::Acct)
56  {
57  env.fund(xrp, account);
58  }
59  }
60  env.close();
61  for (auto const& account : accounts)
62  {
63  for (auto const& amt : amts)
64  {
65  env.trust(amt + amt, account);
66  env(pay(amt.issue().account, account, amt));
67  }
68  }
69  env.close();
70 }
71 
72 void
74  jtx::Env& env,
75  jtx::Account const& gw,
76  std::vector<jtx::Account> const& accounts,
77  STAmount const& xrp,
78  std::vector<STAmount> const& amts,
79  Fund how)
80 {
81  if (how == Fund::All || how == Fund::Gw)
82  env.fund(xrp, gw);
83  env.close();
84  fund(env, accounts, xrp, amts, how);
85 }
86 
88  : gw("gateway")
89  , carol("carol")
90  , alice("alice")
91  , bob("bob")
92  , USD(gw["USD"])
93  , EUR(gw["EUR"])
94  , GBP(gw["GBP"])
95  , BTC(gw["BTC"])
96  , BAD(jtx::IOU(gw, badCurrency()))
97 {
98 }
99 
100 void
102  std::function<void(jtx::AMM&, jtx::Env&)>&& cb,
104  std::uint16_t tfee,
106  std::optional<FeatureBitset> const& features)
107 {
108  using namespace jtx;
109  auto env = [&]() {
110  if (features)
111  return Env{*this, *features};
112  return Env{*this};
113  }();
114 
115  auto const [asset1, asset2] =
116  pool ? *pool : std::make_pair(XRP(10000), USD(10000));
117  auto tofund = [&](STAmount const& a) -> STAmount {
118  if (a.native())
119  {
120  auto const defXRP = XRP(30000);
121  if (a <= defXRP)
122  return defXRP;
123  return a + XRP(1000);
124  }
125  auto const defIOU = STAmount{a.issue(), 30000};
126  if (a <= defIOU)
127  return defIOU;
128  return a + STAmount{a.issue(), 1000};
129  };
130  auto const toFund1 = tofund(asset1);
131  auto const toFund2 = tofund(asset2);
132  BEAST_EXPECT(asset1 <= toFund1 && asset2 <= toFund2);
133 
134  if (!asset1.native() && !asset2.native())
135  fund(env, gw, {alice, carol}, {toFund1, toFund2}, Fund::All);
136  else if (asset1.native())
137  fund(env, gw, {alice, carol}, toFund1, {toFund2}, Fund::All);
138  else if (asset2.native())
139  fund(env, gw, {alice, carol}, toFund2, {toFund1}, Fund::All);
140 
141  AMM ammAlice(env, alice, asset1, asset2, false, tfee);
142  BEAST_EXPECT(ammAlice.expectBalances(asset1, asset2, ammAlice.tokens()));
143  cb(ammAlice, env);
144 }
145 
146 XRPAmount
148 {
149  return env.current()->fees().accountReserve(count);
150 }
151 
152 XRPAmount
154 {
155  return env.current()->fees().increment;
156 }
157 
158 jtx::Env
160 {
161  // These tests were originally written with search parameters that are
162  // different from the current defaults. This function creates an env
163  // with the search parameters that the tests were written for.
164  return Env(*this, envconfig([](std::unique_ptr<Config> cfg) {
165  cfg->PATH_SEARCH_OLD = 7;
166  cfg->PATH_SEARCH = 7;
167  cfg->PATH_SEARCH_MAX = 10;
168  return cfg;
169  }));
170 }
171 
174  jtx::Env& env,
175  jtx::Account const& src,
176  jtx::Account const& dst,
177  STAmount const& saDstAmount,
178  std::optional<STAmount> const& saSendMax,
179  std::optional<Currency> const& saSrcCurrency)
180 {
181  using namespace jtx;
182 
183  auto& app = env.app();
186 
187  RPC::JsonContext context{
188  {env.journal,
189  app,
190  loadType,
191  app.getOPs(),
192  app.getLedgerMaster(),
193  c,
194  Role::USER,
195  {},
196  {},
198  {},
199  {}};
200 
202  params[jss::command] = "ripple_path_find";
203  params[jss::source_account] = toBase58(src);
204  params[jss::destination_account] = toBase58(dst);
205  params[jss::destination_amount] = saDstAmount.getJson(JsonOptions::none);
206  if (saSendMax)
207  params[jss::send_max] = saSendMax->getJson(JsonOptions::none);
208  if (saSrcCurrency)
209  {
210  auto& sc = params[jss::source_currencies] = Json::arrayValue;
212  j[jss::currency] = to_string(saSrcCurrency.value());
213  sc.append(j);
214  }
215 
216  Json::Value result;
217  gate g;
218  app.getJobQueue().postCoro(jtCLIENT, "RPC-Client", [&](auto const& coro) {
219  context.params = std::move(params);
220  context.coro = coro;
221  RPC::doCommand(context, result);
222  g.signal();
223  });
224 
225  using namespace std::chrono_literals;
226  BEAST_EXPECT(g.wait_for(5s));
227  BEAST_EXPECT(!result.isMember(jss::error));
228  return result;
229 }
230 
233  jtx::Env& env,
234  jtx::Account const& src,
235  jtx::Account const& dst,
236  STAmount const& saDstAmount,
237  std::optional<STAmount> const& saSendMax,
238  std::optional<Currency> const& saSrcCurrency)
239 {
240  Json::Value result = find_paths_request(
241  env, src, dst, saDstAmount, saSendMax, saSrcCurrency);
242  BEAST_EXPECT(!result.isMember(jss::error));
243 
244  STAmount da;
245  if (result.isMember(jss::destination_amount))
246  da = amountFromJson(sfGeneric, result[jss::destination_amount]);
247 
248  STAmount sa;
250  if (result.isMember(jss::alternatives))
251  {
252  auto const& alts = result[jss::alternatives];
253  if (alts.size() > 0)
254  {
255  auto const& path = alts[0u];
256 
257  if (path.isMember(jss::source_amount))
258  sa = amountFromJson(sfGeneric, path[jss::source_amount]);
259 
260  if (path.isMember(jss::destination_amount))
261  da = amountFromJson(sfGeneric, path[jss::destination_amount]);
262 
263  if (path.isMember(jss::paths_computed))
264  {
265  Json::Value p;
266  p["Paths"] = path[jss::paths_computed];
267  STParsedJSONObject po("generic", p);
268  paths = po.object->getFieldPathSet(sfPaths);
269  }
270  }
271  }
272 
273  return std::make_tuple(std::move(paths), std::move(sa), std::move(da));
274 }
275 
276 } // namespace jtx
277 } // namespace test
278 } // namespace ripple
ripple::badCurrency
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
Definition: UintTypes.cpp:129
ripple::test::jtx::AMMTestBase::USD
const jtx::IOU USD
Definition: AMMTest.h:68
ripple::RPC::apiVersionIfUnspecified
constexpr unsigned int apiVersionIfUnspecified
Definition: RPCHelpers.h:241
ripple::sfPaths
const SField sfPaths
ripple::RPC::JsonContext
Definition: Context.h:53
std::make_tuple
T make_tuple(T... args)
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::test::jtx::AMMTest::ammCrtFee
XRPAmount ammCrtFee(jtx::Env &env) const
Definition: AMMTest.cpp:153
ripple::jtCLIENT
@ jtCLIENT
Definition: Job.h:45
ripple::sfGeneric
const SField sfGeneric(access, 0)
Definition: SField.h:356
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:350
ripple::test::jtx::ter
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:33
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
std::pair
std::vector
STL class.
ripple::test::jtx::AMMTest::gate::wait_for
bool wait_for(std::chrono::duration< Rep, Period > const &rel_time)
Definition: AMMTest.h:113
ripple::STAmount::getJson
Json::Value getJson(JsonOptions) const override
Definition: STAmount.cpp:653
ripple::test::jtx::Fund::Acct
@ Acct
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
ripple::test::jtx::AMMTestBase::gw
const jtx::Account gw
Definition: AMMTest.h:64
ripple::test::jtx::AMMTestBase::carol
const jtx::Account carol
Definition: AMMTest.h:65
ripple::test::jtx::Env::journal
const beast::Journal journal
Definition: Env.h:145
ripple::test::jtx::AMMTestBase::alice
const jtx::Account alice
Definition: AMMTest.h:66
ripple::STParsedJSONObject
Holds the serialized result of parsing an input JSON object.
Definition: STParsedJSON.h:31
ripple::Resource::feeReferenceRPC
const Charge feeReferenceRPC
std::tuple
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:242
ripple::test::jtx::AMM
Convenience class to test AMM functionality.
Definition: AMM.h:62
std::function
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::STParsedJSONObject::object
std::optional< STObject > object
The STObject if the parse was successful.
Definition: STParsedJSON.h:50
ripple::STPathSet
Definition: STPathSet.h:176
ripple::test::jtx::Env::trust
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:259
ripple::test::jtx::AMMTest::find_paths
std::tuple< STPathSet, STAmount, STAmount > find_paths(jtx::Env &env, jtx::Account const &src, jtx::Account const &dst, STAmount const &saDstAmount, std::optional< STAmount > const &saSendMax=std::nullopt, std::optional< Currency > const &saSrcCurrency=std::nullopt)
Definition: AMMTest.cpp:232
ripple::RPC::doCommand
Status doCommand(RPC::JsonContext &context, Json::Value &result)
Execute an RPC command and store the results in a Json::Value.
Definition: RPCHandler.cpp:250
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::Role::USER
@ USER
ripple::test::jtx::AMMTest::gate
Definition: AMMTest.h:101
ripple::test::jtx::Fund::All
@ All
ripple::JsonOptions::none
@ none
ripple::test::jtx::Fund
Fund
Definition: AMMTest.h:34
ripple::test::jtx::AMMTest::reserve
XRPAmount reserve(jtx::Env &env, std::uint32_t count) const
Definition: AMMTest.cpp:147
ripple::test::jtx::paths
Set Paths, SendMax on a JTx.
Definition: paths.h:32
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::STAmount
Definition: STAmount.h:46
ripple::test::jtx::Fund::Gw
@ Gw
ripple::amountFromJson
STAmount amountFromJson(SField const &name, Json::Value const &v)
Definition: STAmount.cpp:927
ripple::test::jtx::path
Add a path.
Definition: paths.h:55
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
ripple::test::jtx::AMMTest::pathTestEnv
jtx::Env pathTestEnv()
Definition: AMMTest.cpp:159
std::uint16_t
ripple::test::jtx::AMMTestBase::AMMTestBase
AMMTestBase()
Definition: AMMTest.cpp:87
std::optional::value
T value(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::jtx::IOU
Converts to IOU Issue or STAmount.
Definition: amount.h:291
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:228
ripple::Resource::Consumer
An endpoint that consumes resources.
Definition: Consumer.h:34
ripple::Resource::Charge
A consumption charge.
Definition: Charge.h:30
ripple::test::jtx::fund
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:35
ripple::test::jtx::pay
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
std::optional
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
std::make_pair
T make_pair(T... args)
std::unique_ptr
STL class.
ripple::test::jtx::AMMTest::find_paths_request
Json::Value find_paths_request(jtx::Env &env, jtx::Account const &src, jtx::Account const &dst, STAmount const &saDstAmount, std::optional< STAmount > const &saSendMax=std::nullopt, std::optional< Currency > const &saSrcCurrency=std::nullopt)
Definition: AMMTest.cpp:173
ripple::test::jtx::AMMTestBase::testAMM
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::optional< FeatureBitset > const &features=std::nullopt)
testAMM() funds 30,000XRP and 30,000IOU for each non-XRP asset to Alice and Carol
Definition: AMMTest.cpp:101
ripple::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:301
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:117
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::test::jtx::AMMTest::gate::signal
void signal()
Definition: AMMTest.h:122