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#include <xrpld/rpc/detail/RPCHelpers.h>
22#include <xrpl/protocol/Feature.h>
23#include <xrpl/protocol/jss.h>
24
25namespace ripple {
26
27namespace test {
28
30{
31public:
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 {
285
286 auto withFeatsTests = [this](FeatureBitset features) {
287 forAllApiVersions([&, this](unsigned testVersion) {
288 testDefaultRipple(features, testVersion);
289 });
290 testNegativeBalance(features);
291 testPairwise(features);
292 };
293 using namespace jtx;
294 auto const sa = supported_amendments();
295 withFeatsTests(sa - featureFlowCross);
296 withFeatsTests(sa);
297 }
298};
299
300BEAST_DEFINE_TESTSUITE(NoRipple, app, ripple);
301
302} // namespace test
303} // namespace ripple
Represents a JSON value.
Definition: json_value.h:147
A testsuite class.
Definition: suite.h:53
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:153
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:38
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
A transaction testing environment.
Definition: Env.h:117
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:268
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:749
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:237
Add a path.
Definition: paths.h:56
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:34
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:30
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:28
Json::Value getAccountLines(Env &env, AccountID const &acctId)
Definition: TestHelpers.cpp:40
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:104
FeatureBitset supported_amendments()
Definition: Env.h:70
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t tfClearNoRipple
Definition: TxFlags.h:114
constexpr std::uint32_t asfDefaultRipple
Definition: TxFlags.h:83
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition: ApiVersion.h:102
@ tecNO_PERMISSION
Definition: TER.h:292
@ tecPATH_DRY
Definition: TER.h:281
@ tesSUCCESS
Definition: TER.h:242
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:113
T to_string(T... args)