rippled
NoRipple_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2016 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 <ripple/protocol/Feature.h>
21 #include <ripple/protocol/jss.h>
22 #include <ripple/rpc/impl/RPCHelpers.h>
23 #include <test/jtx.h>
24 
25 namespace ripple {
26 
27 namespace test {
28 
29 class NoRipple_test : public beast::unit_test::suite
30 {
31 public:
32  void
34  {
35  testcase("Set and clear noripple");
36 
37  using namespace jtx;
38  Env env(*this);
39 
40  auto const gw = Account("gateway");
41  auto const alice = Account("alice");
42 
43  env.fund(XRP(10000), gw, alice);
44 
45  auto const USD = gw["USD"];
46 
47  Json::Value account_gw;
48  account_gw[jss::account] = gw.human();
49  Json::Value account_alice;
50  account_alice[jss::account] = alice.human();
51 
52  for (auto SetOrClear : {true, false})
53  {
54  // Create a trust line with no-ripple flag setting
55  env(trust(
56  gw,
57  USD(100),
58  alice,
59  SetOrClear ? tfSetNoRipple : tfClearNoRipple));
60  env.close();
61 
62  // Check no-ripple flag on sender 'gateway'
64  env.rpc("json", "account_lines", to_string(account_gw))};
65  auto const& gline0 = lines[jss::result][jss::lines][0u];
66  BEAST_EXPECT(gline0[jss::no_ripple].asBool() == SetOrClear);
67 
68  // Check no-ripple peer flag on destination 'alice'
69  lines = env.rpc("json", "account_lines", to_string(account_alice));
70  auto const& aline0 = lines[jss::result][jss::lines][0u];
71  BEAST_EXPECT(aline0[jss::no_ripple_peer].asBool() == SetOrClear);
72  }
73  }
74 
75  void
77  {
78  testcase("Set noripple on a line with negative balance");
79 
80  using namespace jtx;
81  auto const gw = Account("gateway");
82  auto const alice = Account("alice");
83  auto const bob = Account("bob");
84  auto const carol = Account("carol");
85 
86  // fix1578 changes the return code. Verify expected behavior
87  // without and with fix1578.
88  for (auto const& tweakedFeatures :
89  {features - fix1578, features | fix1578})
90  {
91  Env env(*this, tweakedFeatures);
92 
93  env.fund(XRP(10000), gw, alice, bob, carol);
94  env.close();
95 
96  env.trust(alice["USD"](100), bob);
97  env.trust(bob["USD"](100), carol);
98  env.close();
99 
100  // After this payment alice has a -50 USD balance with bob, and
101  // bob has a -50 USD balance with carol. So neither alice nor
102  // bob should be able to clear the noRipple flag.
103  env(pay(alice, carol, carol["USD"](50)), path(bob));
104  env.close();
105 
106  TER const terNeg{
107  tweakedFeatures[fix1578] ? TER{tecNO_PERMISSION}
108  : TER{tesSUCCESS}};
109 
110  env(trust(alice, bob["USD"](100), bob, tfSetNoRipple), ter(terNeg));
111  env(trust(bob, carol["USD"](100), carol, tfSetNoRipple),
112  ter(terNeg));
113  env.close();
114 
115  Json::Value params;
116  params[jss::source_account] = alice.human();
117  params[jss::destination_account] = carol.human();
118  params[jss::destination_amount] = [] {
119  Json::Value dest_amt;
120  dest_amt[jss::currency] = "USD";
121  dest_amt[jss::value] = "1";
122  dest_amt[jss::issuer] = Account("carol").human();
123  return dest_amt;
124  }();
125 
126  auto const resp =
127  env.rpc("json", "ripple_path_find", to_string(params));
128  BEAST_EXPECT(resp[jss::result][jss::alternatives].size() == 1);
129 
130  auto getAccountLines = [&env](Account const& acct) {
131  auto const r = jtx::getAccountLines(env, acct);
132  return r[jss::lines];
133  };
134  {
135  auto const aliceLines = getAccountLines(alice);
136  BEAST_EXPECT(aliceLines.size() == 1);
137  BEAST_EXPECT(aliceLines[0u][jss::no_ripple].asBool() == false);
138 
139  auto const bobLines = getAccountLines(bob);
140  BEAST_EXPECT(bobLines.size() == 2);
141  BEAST_EXPECT(bobLines[0u][jss::no_ripple].asBool() == false);
142  BEAST_EXPECT(bobLines[1u][jss::no_ripple].asBool() == false);
143  }
144 
145  // Now carol sends the 50 USD back to alice. Then alice and
146  // bob can set the noRipple flag.
147  env(pay(carol, alice, alice["USD"](50)), path(bob));
148  env.close();
149 
150  env(trust(alice, bob["USD"](100), bob, tfSetNoRipple));
151  env(trust(bob, carol["USD"](100), carol, tfSetNoRipple));
152  env.close();
153  {
154  auto const aliceLines = getAccountLines(alice);
155  BEAST_EXPECT(aliceLines.size() == 1);
156  BEAST_EXPECT(aliceLines[0u].isMember(jss::no_ripple));
157 
158  auto const bobLines = getAccountLines(bob);
159  BEAST_EXPECT(bobLines.size() == 2);
160  BEAST_EXPECT(bobLines[0u].isMember(jss::no_ripple_peer));
161  BEAST_EXPECT(bobLines[1u].isMember(jss::no_ripple));
162  }
163  }
164  }
165 
166  void
168  {
169  testcase("pairwise NoRipple");
170 
171  using namespace jtx;
172  Env env(*this, features);
173 
174  auto const alice = Account("alice");
175  auto const bob = Account("bob");
176  auto const carol = Account("carol");
177 
178  env.fund(XRP(10000), alice, bob, carol);
179 
180  env(trust(bob, alice["USD"](100)));
181  env(trust(carol, bob["USD"](100)));
182 
183  env(trust(bob, alice["USD"](100), alice, tfSetNoRipple));
184  env(trust(bob, carol["USD"](100), carol, tfSetNoRipple));
185  env.close();
186 
187  Json::Value params;
188  params[jss::source_account] = alice.human();
189  params[jss::destination_account] = carol.human();
190  params[jss::destination_amount] = [] {
191  Json::Value dest_amt;
192  dest_amt[jss::currency] = "USD";
193  dest_amt[jss::value] = "1";
194  dest_amt[jss::issuer] = Account("carol").human();
195  return dest_amt;
196  }();
197 
198  Json::Value const resp{
199  env.rpc("json", "ripple_path_find", to_string(params))};
200  BEAST_EXPECT(resp[jss::result][jss::alternatives].size() == 0);
201 
202  env(pay(alice, carol, bob["USD"](50)), ter(tecPATH_DRY));
203  }
204 
205  void
206  testDefaultRipple(FeatureBitset features, unsigned int apiVersion)
207  {
208  testcase(
209  "Set default ripple on an account and check new trustlines "
210  "Version " +
211  std::to_string(apiVersion));
212 
213  using namespace jtx;
214  Env env(*this, features);
215 
216  auto const gw = Account("gateway");
217  auto const alice = Account("alice");
218  auto const bob = Account("bob");
219 
220  env.fund(XRP(10000), gw, noripple(alice, bob));
221 
222  env(fset(bob, asfDefaultRipple));
223 
224  auto const USD = gw["USD"];
225 
226  env(trust(gw, USD(100), alice, 0));
227  env(trust(gw, USD(100), bob, 0));
228  Json::Value params;
229  params[jss::api_version] = apiVersion;
230 
231  {
232  params[jss::account] = gw.human();
233  params[jss::peer] = alice.human();
234 
235  auto lines = env.rpc("json", "account_lines", to_string(params));
236  auto const& line0 = lines[jss::result][jss::lines][0u];
237  BEAST_EXPECT(line0[jss::no_ripple_peer].asBool() == true);
238  }
239  {
240  params[jss::account] = alice.human();
241  params[jss::peer] = gw.human();
242 
243  auto lines = env.rpc("json", "account_lines", to_string(params));
244  auto const& line0 = lines[jss::result][jss::lines][0u];
245  BEAST_EXPECT(line0[jss::no_ripple].asBool() == true);
246  }
247  {
248  params[jss::account] = gw.human();
249  params[jss::peer] = bob.human();
250 
251  auto lines = env.rpc("json", "account_lines", to_string(params));
252  auto const& line0 = lines[jss::result][jss::lines][0u];
253  BEAST_EXPECT(line0[jss::no_ripple].asBool() == false);
254  }
255  {
256  params[jss::account] = bob.human();
257  params[jss::peer] = gw.human();
258 
259  auto lines = env.rpc("json", "account_lines", to_string(params));
260  auto const& line0 = lines[jss::result][jss::lines][0u];
261  BEAST_EXPECT(line0[jss::no_ripple_peer].asBool() == false);
262  }
263  {
264  // test for transactions
265  {
266  params[jss::account] = bob.human();
267  params[jss::role] = "gateway";
268  params[jss::transactions] = "asdf";
269 
270  auto lines =
271  env.rpc("json", "noripple_check", to_string(params));
272  if (apiVersion < 2u)
273  BEAST_EXPECT(lines[jss::result][jss::status] == "success");
274  else
275  BEAST_EXPECT(
276  lines[jss::result][jss::error] == "invalidParams");
277  }
278  }
279  }
280 
281  void
282  run() override
283  {
284  testSetAndClear();
285 
286  auto withFeatsTests = [this](FeatureBitset features) {
287  for (auto testVersion = RPC::apiMinimumSupportedVersion;
288  testVersion <= RPC::apiBetaVersion;
289  ++testVersion)
290  {
291  testDefaultRipple(features, testVersion);
292  }
293  testNegativeBalance(features);
294  testPairwise(features);
295  };
296  using namespace jtx;
297  auto const sa = supported_amendments();
298  withFeatsTests(sa - featureFlowCross);
299  withFeatsTests(sa);
300  }
301 };
302 
303 BEAST_DEFINE_TESTSUITE(NoRipple, app, ripple);
304 
305 } // namespace test
306 } // namespace ripple
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
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
ripple::tfSetNoRipple
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:110
ripple::test::jtx::Account::human
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
ripple::RPC::apiBetaVersion
constexpr unsigned int apiBetaVersion
Definition: RPCHelpers.h:247
ripple::test::jtx::Env::trust
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:259
ripple::test::NoRipple_test::testSetAndClear
void testSetAndClear()
Definition: NoRipple_test.cpp:33
ripple::TERSubset< CanCvtToTER >
ripple::test::jtx::fset
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:28
ripple::test::NoRipple_test::testNegativeBalance
void testNegativeBalance(FeatureBitset features)
Definition: NoRipple_test.cpp:76
std::to_string
T to_string(T... args)
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::tfClearNoRipple
constexpr std::uint32_t tfClearNoRipple
Definition: TxFlags.h:111
ripple::test::jtx::path
Add a path.
Definition: paths.h:55
ripple::test::jtx::supported_amendments
FeatureBitset supported_amendments()
Definition: Env.h:71
ripple::fix1578
const uint256 fix1578
ripple::RPC::apiMinimumSupportedVersion
constexpr unsigned int apiMinimumSupportedVersion
Definition: RPCHelpers.h:245
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::jtx::noripple
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
Definition: Env.h:65
ripple::test::NoRipple_test::testPairwise
void testPairwise(FeatureBitset features)
Definition: NoRipple_test.cpp:167
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:228
ripple::asfDefaultRipple
constexpr std::uint32_t asfDefaultRipple
Definition: TxFlags.h:81
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:276
ripple::FeatureBitset
Definition: Feature.h:113
ripple::test::NoRipple_test::run
void run() override
Definition: NoRipple_test.cpp:282
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:265
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
ripple::test::NoRipple_test
Definition: NoRipple_test.cpp:29
ripple::test::NoRipple_test::testDefaultRipple
void testDefaultRipple(FeatureBitset features, unsigned int apiVersion)
Definition: NoRipple_test.cpp:206
ripple::featureFlowCross
const uint256 featureFlowCross
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:226
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:117
ripple::test::jtx::getAccountLines
Json::Value getAccountLines(Env &env, AccountID const &acctId)
Definition: TestHelpers.cpp:40
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:700
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::test::jtx::owner_count
Definition: owners.h:49
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)