rippled
Loading...
Searching...
No Matches
DeliveredAmount_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2019 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/WSClient.h>
22#include <xrpl/beast/unit_test.h>
23#include <xrpl/protocol/Feature.h>
24#include <xrpl/protocol/jss.h>
25
26namespace ripple {
27namespace test {
28
29// Helper class to track the expected number `delivered_amount` results.
31{
32 // If the test occurs before or after the switch time
34 // number of payments expected 'delivered_amount' available
36 // Number of payments with field with `delivered_amount` set to the
37 // string "unavailable"
39 // Number of payments with no `delivered_amount` field
41
42 // Increment one of the expected numExpected{Available_, Unavailable_,
43 // NotSet_} values. Which value to increment depends on: 1) If the ledger is
44 // before or after the switch time 2) If the tx is a partial payment 3) If
45 // the payment is successful or not
46 void
47 adjCounters(bool success, bool partial)
48 {
49 if (!success)
50 {
52 return;
53 }
55 {
56 if (partial)
58 else
60 return;
61 }
62 // normal case: after switch time & successful transaction
64 }
65
66public:
67 explicit CheckDeliveredAmount(bool afterSwitchTime)
68 : afterSwitchTime_(afterSwitchTime)
69 {
70 }
71
72 void
74 {
75 adjCounters(true, false);
76 }
77
78 void
80 {
81 adjCounters(false, false);
82 }
83 void
85 {
86 adjCounters(true, true);
87 }
88
89 // After all the txns are checked, all the `numExpected` variables should be
90 // zero. The `checkTxn` function decrements these variables.
91 bool
93 {
96 }
97
98 // Check if the transaction has `delivered_amount` in the metaData as
99 // expected from our rules. Decrements the appropriate `numExpected`
100 // variable. After all the txns are checked, all the `numExpected` variables
101 // should be zero.
102 bool
103 checkTxn(Json::Value const& t, Json::Value const& metaData)
104 {
105 if (t[jss::TransactionType].asString() != jss::Payment)
106 return true;
107
108 bool isSet = metaData.isMember(jss::delivered_amount);
109 bool isSetUnavailable = false;
110 bool isSetAvailable = false;
111 if (isSet)
112 {
113 if (metaData[jss::delivered_amount] != "unavailable")
114 isSetAvailable = true;
115 else
116 isSetUnavailable = true;
117 }
118 if (isSetAvailable)
120 else if (isSetUnavailable)
122 else if (!isSet)
124
125 if (isSet)
126 {
127 if (metaData.isMember(sfDeliveredAmount.jsonName))
128 {
129 if (metaData[jss::delivered_amount] !=
130 metaData[sfDeliveredAmount.jsonName])
131 return false;
132 }
133 else
134 {
136 {
137 if (metaData[jss::delivered_amount] != t[jss::Amount])
138 return false;
139 }
140 else
141 {
142 if (metaData[jss::delivered_amount] != "unavailable")
143 return false;
144 }
145 }
146 }
147
148 if (metaData[sfTransactionResult.jsonName] != "tesSUCCESS")
149 {
150 if (isSet)
151 return false;
152 }
153 else
154 {
156 {
157 if (!isSetAvailable)
158 return false;
159 }
160 else
161 {
162 if (metaData.isMember(sfDeliveredAmount.jsonName))
163 {
164 if (!isSetAvailable)
165 return false;
166 }
167 else
168 {
169 if (!isSetUnavailable)
170 return false;
171 }
172 }
173 }
174 return true;
175 }
176};
177
179{
180 void
182 {
183 testcase("Ledger Request Subscribe DeliveredAmount");
184
185 using namespace test::jtx;
186 using namespace std::chrono_literals;
187
188 Account const alice("alice");
189 Account const bob("bob");
190 Account const carol("carol");
191 auto const gw = Account("gateway");
192 auto const USD = gw["USD"];
193
194 for (bool const afterSwitchTime : {true, false})
195 {
196 Env env{*this};
197 env.fund(XRP(10000), alice, bob, carol, gw);
198 env.trust(USD(1000), alice, bob, carol);
199 if (afterSwitchTime)
200 env.close(NetClock::time_point{446000000s});
201 else
202 env.close();
203
204 CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
205 {
206 // add payments, but do no close until subscribed
207
208 // normal payments
209 env(pay(gw, alice, USD(50)));
210 checkDeliveredAmount.adjCountersSuccess();
211 env(pay(gw, alice, XRP(50)));
212 checkDeliveredAmount.adjCountersSuccess();
213
214 // partial payment
215 env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
216 checkDeliveredAmount.adjCountersPartialPayment();
217 env.require(balance(bob, USD(1000)));
218
219 // failed payment
220 env(pay(bob, carol, USD(9999999)), ter(tecPATH_PARTIAL));
221 checkDeliveredAmount.adjCountersFail();
222 env.require(balance(carol, USD(0)));
223 }
224
225 auto wsc = makeWSClient(env.app().config());
226
227 {
228 Json::Value stream;
229 // RPC subscribe to ledger stream
230 stream[jss::streams] = Json::arrayValue;
231 stream[jss::streams].append("ledger");
232 stream[jss::accounts] = Json::arrayValue;
233 stream[jss::accounts].append(toBase58(alice.id()));
234 stream[jss::accounts].append(toBase58(bob.id()));
235 stream[jss::accounts].append(toBase58(carol.id()));
236 auto jv = wsc->invoke("subscribe", stream);
237 if (wsc->version() == 2)
238 {
239 BEAST_EXPECT(
240 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
241 BEAST_EXPECT(
242 jv.isMember(jss::ripplerpc) &&
243 jv[jss::ripplerpc] == "2.0");
244 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
245 }
246 BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 3);
247 }
248 {
249 env.close();
250 // Check stream update
251 while (true)
252 {
253 auto const r = wsc->findMsg(1s, [&](auto const& jv) {
254 return jv[jss::ledger_index] == 4;
255 });
256 if (!r)
257 break;
258
259 if (!r->isMember(jss::transaction))
260 continue;
261
262 BEAST_EXPECT(checkDeliveredAmount.checkTxn(
263 (*r)[jss::transaction], (*r)[jss::meta]));
264 }
265 }
266 BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
267 }
268 }
269 void
271 {
272 testcase("Ledger Request RPC DeliveredAmount");
273
274 using namespace test::jtx;
275 using namespace std::chrono_literals;
276
277 Account const alice("alice");
278 Account const bob("bob");
279 Account const carol("carol");
280 auto const gw = Account("gateway");
281 auto const USD = gw["USD"];
282
283 for (bool const afterSwitchTime : {true, false})
284 {
285 Env env{*this};
286 env.fund(XRP(10000), alice, bob, carol, gw);
287 env.trust(USD(1000), alice, bob, carol);
288 if (afterSwitchTime)
289 env.close(NetClock::time_point{446000000s});
290 else
291 env.close();
292
293 CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
294 // normal payments
295 env(pay(gw, alice, USD(50)));
296 checkDeliveredAmount.adjCountersSuccess();
297 env(pay(gw, alice, XRP(50)));
298 checkDeliveredAmount.adjCountersSuccess();
299
300 // partial payment
301 env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
302 checkDeliveredAmount.adjCountersPartialPayment();
303 env.require(balance(bob, USD(1000)));
304
305 // failed payment
306 env(pay(gw, carol, USD(9999999)), ter(tecPATH_PARTIAL));
307 checkDeliveredAmount.adjCountersFail();
308 env.require(balance(carol, USD(0)));
309
310 env.close();
311 std::string index;
312 Json::Value jvParams;
313 jvParams[jss::ledger_index] = 4u;
314 jvParams[jss::transactions] = true;
315 jvParams[jss::expand] = true;
316 auto const jtxn = env.rpc(
317 "json",
318 "ledger",
319 to_string(
320 jvParams))[jss::result][jss::ledger][jss::transactions];
321 for (auto const& t : jtxn)
322 BEAST_EXPECT(
323 checkDeliveredAmount.checkTxn(t, t[jss::metaData]));
324 BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
325 }
326 }
327
328public:
329 void
330 run() override
331 {
334 }
335};
336
337BEAST_DEFINE_TESTSUITE(DeliveredAmount, app, ripple);
338
339} // namespace test
340} // namespace ripple
Represents a JSON value.
Definition: json_value.h:147
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:943
A testsuite class.
Definition: suite.h:53
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:153
void adjCounters(bool success, bool partial)
bool checkTxn(Json::Value const &t, Json::Value const &metaData)
void run() override
Runs the suite.
Immutable cryptographic account descriptor.
Definition: Account.h:38
AccountID id() const
Returns the Account ID.
Definition: Account.h:106
A transaction testing environment.
Definition: Env.h:117
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:237
A balance matches.
Definition: balance.h:39
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:34
Set the flags on a JTx.
Definition: txflags.h:31
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
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
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Definition: WSClient.cpp:304
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:106
constexpr std::uint32_t tfPartialPayment
Definition: TxFlags.h:105
@ tecPATH_PARTIAL
Definition: TER.h:269
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629