rippled
Loading...
Searching...
No Matches
DeliveredAmount_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/WSClient.h>
3
4#include <xrpl/beast/unit_test.h>
5#include <xrpl/beast/unit_test/suite.h>
6#include <xrpl/protocol/jss.h>
7
8namespace ripple {
9namespace test {
10
11// Helper class to track the expected number `delivered_amount` results.
13{
14 // If the test occurs before or after the switch time
16 // number of payments expected 'delivered_amount' available
18 // Number of payments with field with `delivered_amount` set to the
19 // string "unavailable"
21 // Number of payments with no `delivered_amount` field
23
24 // Increment one of the expected numExpected{Available_, Unavailable_,
25 // NotSet_} values. Which value to increment depends on: 1) If the ledger is
26 // before or after the switch time 2) If the tx is a partial payment 3) If
27 // the payment is successful or not
28 void
29 adjCounters(bool success, bool partial)
30 {
31 if (!success)
32 {
34 return;
35 }
37 {
38 if (partial)
40 else
42 return;
43 }
44 // normal case: after switch time & successful transaction
46 }
47
48public:
49 explicit CheckDeliveredAmount(bool afterSwitchTime)
50 : afterSwitchTime_(afterSwitchTime)
51 {
52 }
53
54 void
56 {
57 adjCounters(true, false);
58 }
59
60 void
62 {
63 adjCounters(false, false);
64 }
65 void
67 {
68 adjCounters(true, true);
69 }
70
71 // After all the txns are checked, all the `numExpected` variables should be
72 // zero. The `checkTxn` function decrements these variables.
73 bool
79
80 // Check if the transaction has `delivered_amount` in the metaData as
81 // expected from our rules. Decrements the appropriate `numExpected`
82 // variable. After all the txns are checked, all the `numExpected` variables
83 // should be zero.
84 bool
85 checkTxn(Json::Value const& t, Json::Value const& metaData)
86 {
87 if (t[jss::TransactionType].asString() != jss::Payment)
88 return true;
89
90 bool isSet = metaData.isMember(jss::delivered_amount);
91 bool isSetUnavailable = false;
92 bool isSetAvailable = false;
93 if (isSet)
94 {
95 if (metaData[jss::delivered_amount] != "unavailable")
96 isSetAvailable = true;
97 else
98 isSetUnavailable = true;
99 }
100 if (isSetAvailable)
102 else if (isSetUnavailable)
104 else if (!isSet)
106
107 if (isSet)
108 {
109 if (metaData.isMember(sfDeliveredAmount.jsonName))
110 {
111 if (metaData[jss::delivered_amount] !=
112 metaData[sfDeliveredAmount.jsonName])
113 return false;
114 }
115 else
116 {
118 {
119 if (metaData[jss::delivered_amount] != t[jss::Amount])
120 return false;
121 }
122 else
123 {
124 if (metaData[jss::delivered_amount] != "unavailable")
125 return false;
126 }
127 }
128 }
129
130 if (metaData[sfTransactionResult.jsonName] != "tesSUCCESS")
131 {
132 if (isSet)
133 return false;
134 }
135 else
136 {
138 {
139 if (!isSetAvailable)
140 return false;
141 }
142 else
143 {
144 if (metaData.isMember(sfDeliveredAmount.jsonName))
145 {
146 if (!isSetAvailable)
147 return false;
148 }
149 else
150 {
151 if (!isSetUnavailable)
152 return false;
153 }
154 }
155 }
156 return true;
157 }
158};
159
161{
162 void
164 {
165 testcase("Ledger Request Subscribe DeliveredAmount");
166
167 using namespace test::jtx;
168 using namespace std::chrono_literals;
169
170 Account const alice("alice");
171 Account const bob("bob");
172 Account const carol("carol");
173 auto const gw = Account("gateway");
174 auto const USD = gw["USD"];
175
176 for (bool const afterSwitchTime : {true, false})
177 {
178 auto cfg = envconfig();
179 cfg->FEES.reference_fee = 10;
180 Env env(*this, std::move(cfg));
181 env.fund(XRP(10000), alice, bob, carol, gw);
182 env.trust(USD(1000), alice, bob, carol);
183 if (afterSwitchTime)
184 env.close(NetClock::time_point{446000000s});
185 else
186 env.close();
187
188 CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
189 {
190 // add payments, but do no close until subscribed
191
192 // normal payments
193 env(pay(gw, alice, USD(50)));
194 checkDeliveredAmount.adjCountersSuccess();
195 env(pay(gw, alice, XRP(50)));
196 checkDeliveredAmount.adjCountersSuccess();
197
198 // partial payment
199 env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
200 checkDeliveredAmount.adjCountersPartialPayment();
201 env.require(balance(bob, USD(1000)));
202
203 // failed payment
204 env(pay(bob, carol, USD(9999999)), ter(tecPATH_PARTIAL));
205 checkDeliveredAmount.adjCountersFail();
206 env.require(balance(carol, USD(0)));
207 }
208
209 auto wsc = makeWSClient(env.app().config());
210
211 {
212 Json::Value stream;
213 // RPC subscribe to ledger stream
214 stream[jss::streams] = Json::arrayValue;
215 stream[jss::streams].append("ledger");
216 stream[jss::accounts] = Json::arrayValue;
217 stream[jss::accounts].append(toBase58(alice.id()));
218 stream[jss::accounts].append(toBase58(bob.id()));
219 stream[jss::accounts].append(toBase58(carol.id()));
220 auto jv = wsc->invoke("subscribe", stream);
221 if (wsc->version() == 2)
222 {
223 BEAST_EXPECT(
224 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
225 BEAST_EXPECT(
226 jv.isMember(jss::ripplerpc) &&
227 jv[jss::ripplerpc] == "2.0");
228 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
229 }
230 BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 3);
231 }
232 {
233 env.close();
234 // Check stream update
235 while (true)
236 {
237 auto const r = wsc->findMsg(1s, [&](auto const& jv) {
238 return jv[jss::ledger_index] == 4;
239 });
240 if (!r)
241 break;
242
243 if (!r->isMember(jss::transaction))
244 continue;
245
246 BEAST_EXPECT(checkDeliveredAmount.checkTxn(
247 (*r)[jss::transaction], (*r)[jss::meta]));
248 }
249 }
250 BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
251 }
252 }
253 void
255 {
256 testcase("Ledger Request RPC DeliveredAmount");
257
258 using namespace test::jtx;
259 using namespace std::chrono_literals;
260
261 Account const alice("alice");
262 Account const bob("bob");
263 Account const carol("carol");
264 auto const gw = Account("gateway");
265 auto const USD = gw["USD"];
266
267 for (bool const afterSwitchTime : {true, false})
268 {
269 auto cfg = envconfig();
270 cfg->FEES.reference_fee = 10;
271 Env env(*this, std::move(cfg));
272 env.fund(XRP(10000), alice, bob, carol, gw);
273 env.trust(USD(1000), alice, bob, carol);
274 if (afterSwitchTime)
275 env.close(NetClock::time_point{446000000s});
276 else
277 env.close();
278
279 CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
280 // normal payments
281 env(pay(gw, alice, USD(50)));
282 checkDeliveredAmount.adjCountersSuccess();
283 env(pay(gw, alice, XRP(50)));
284 checkDeliveredAmount.adjCountersSuccess();
285
286 // partial payment
287 env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
288 checkDeliveredAmount.adjCountersPartialPayment();
289 env.require(balance(bob, USD(1000)));
290
291 // failed payment
292 env(pay(gw, carol, USD(9999999)), ter(tecPATH_PARTIAL));
293 checkDeliveredAmount.adjCountersFail();
294 env.require(balance(carol, USD(0)));
295
296 env.close();
297 std::string index;
298 Json::Value jvParams;
299 jvParams[jss::ledger_index] = 4u;
300 jvParams[jss::transactions] = true;
301 jvParams[jss::expand] = true;
302 auto const jtxn = env.rpc(
303 "json",
304 "ledger",
305 to_string(
306 jvParams))[jss::result][jss::ledger][jss::transactions];
307 for (auto const& t : jtxn)
308 BEAST_EXPECT(
309 checkDeliveredAmount.checkTxn(t, t[jss::metaData]));
310 BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
311 }
312 }
313
314 void
316 {
317 testcase("MPT DeliveredAmount");
318
319 using namespace jtx;
320 Account const alice("alice");
321 Account const carol("carol");
322 Account const bob("bob");
323 Env env{*this, features};
324
325 MPTTester mptAlice(
326 env, alice, {.holders = {bob, carol}, .close = false});
327
328 mptAlice.create(
329 {.transferFee = 25000,
330 .ownerCount = 1,
331 .holderCount = 0,
332 .flags = tfMPTCanTransfer});
333 auto const MPT = mptAlice["MPT"];
334
335 mptAlice.authorize({.account = bob});
336 mptAlice.authorize({.account = carol});
337
338 // issuer to holder
339 mptAlice.pay(alice, bob, 10000);
340
341 // holder to holder
342 env(pay(bob, carol, mptAlice.mpt(1000)), txflags(tfPartialPayment));
343 env.close();
344
345 // Get the hash for the most recent transaction.
346 std::string txHash{
347 env.tx()->getJson(JsonOptions::none)[jss::hash].asString()};
348 Json::Value meta = env.rpc("tx", txHash)[jss::result][jss::meta];
349
350 if (features[fixMPTDeliveredAmount])
351 {
352 BEAST_EXPECT(
353 meta[sfDeliveredAmount.jsonName] ==
355 BEAST_EXPECT(
356 meta[jss::delivered_amount] ==
357 STAmount{MPT(800)}.getJson(JsonOptions::none));
358 }
359 else
360 {
361 BEAST_EXPECT(!meta.isMember(sfDeliveredAmount.jsonName));
362 BEAST_EXPECT(
363 meta[jss::delivered_amount] = Json::Value("unavailable"));
364 }
365
366 env(pay(bob, carol, MPT(1000)),
367 sendmax(MPT(1200)),
369 env.close();
370
371 txHash = env.tx()->getJson(JsonOptions::none)[jss::hash].asString();
372 meta = env.rpc("tx", txHash)[jss::result][jss::meta];
373
374 if (features[fixMPTDeliveredAmount])
375 {
376 BEAST_EXPECT(
377 meta[sfDeliveredAmount.jsonName] ==
379 BEAST_EXPECT(
380 meta[jss::delivered_amount] ==
381 STAmount{MPT(960)}.getJson(JsonOptions::none));
382 }
383 else
384 {
385 BEAST_EXPECT(!meta.isMember(sfDeliveredAmount.jsonName));
386 BEAST_EXPECT(
387 meta[jss::delivered_amount] = Json::Value("unavailable"));
388 }
389 }
390
391public:
392 void
393 run() override
394 {
395 using namespace test::jtx;
397
400
401 testMPTDeliveredAmountRPC(all - fixMPTDeliveredAmount);
403 }
404};
405
406BEAST_DEFINE_TESTSUITE(DeliveredAmount, rpc, ripple);
407
408} // namespace test
409} // namespace ripple
Represents a JSON value.
Definition json_value.h:130
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
virtual Config & config()=0
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:753
void adjCounters(bool success, bool partial)
bool checkTxn(Json::Value const &t, Json::Value const &metaData)
void testMPTDeliveredAmountRPC(FeatureBitset features)
void run() override
Runs the suite.
Immutable cryptographic account descriptor.
Definition Account.h:20
AccountID id() const
Returns the Account ID.
Definition Account.h:92
A transaction testing environment.
Definition Env.h:102
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:528
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:302
Application & app()
Definition Env.h:242
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:772
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:68
Converts to MPT Issue or STAmount.
A balance matches.
Definition balance.h:20
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:16
Sets the SendMax on a JTx.
Definition sendmax.h:14
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:16
Set the flags on a JTx.
Definition txflags.h:12
@ arrayValue
array value (ordered list)
Definition json_value.h:25
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:35
FeatureBitset testable_amendments()
Definition Env.h:55
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
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:6
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
constexpr std::uint32_t const tfMPTCanTransfer
Definition TxFlags.h:133
constexpr std::uint32_t tfPartialPayment
Definition TxFlags.h:89
@ tecPATH_PARTIAL
Definition TER.h:264
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611