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