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