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
23#include <xrpl/beast/unit_test.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 auto cfg = envconfig();
197 cfg->FEES.reference_fee = 10;
198 Env env(*this, std::move(cfg));
199 env.fund(XRP(10000), alice, bob, carol, gw);
200 env.trust(USD(1000), alice, bob, carol);
201 if (afterSwitchTime)
202 env.close(NetClock::time_point{446000000s});
203 else
204 env.close();
205
206 CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
207 {
208 // add payments, but do no close until subscribed
209
210 // normal payments
211 env(pay(gw, alice, USD(50)));
212 checkDeliveredAmount.adjCountersSuccess();
213 env(pay(gw, alice, XRP(50)));
214 checkDeliveredAmount.adjCountersSuccess();
215
216 // partial payment
217 env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
218 checkDeliveredAmount.adjCountersPartialPayment();
219 env.require(balance(bob, USD(1000)));
220
221 // failed payment
222 env(pay(bob, carol, USD(9999999)), ter(tecPATH_PARTIAL));
223 checkDeliveredAmount.adjCountersFail();
224 env.require(balance(carol, USD(0)));
225 }
226
227 auto wsc = makeWSClient(env.app().config());
228
229 {
230 Json::Value stream;
231 // RPC subscribe to ledger stream
232 stream[jss::streams] = Json::arrayValue;
233 stream[jss::streams].append("ledger");
234 stream[jss::accounts] = Json::arrayValue;
235 stream[jss::accounts].append(toBase58(alice.id()));
236 stream[jss::accounts].append(toBase58(bob.id()));
237 stream[jss::accounts].append(toBase58(carol.id()));
238 auto jv = wsc->invoke("subscribe", stream);
239 if (wsc->version() == 2)
240 {
241 BEAST_EXPECT(
242 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
243 BEAST_EXPECT(
244 jv.isMember(jss::ripplerpc) &&
245 jv[jss::ripplerpc] == "2.0");
246 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
247 }
248 BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 3);
249 }
250 {
251 env.close();
252 // Check stream update
253 while (true)
254 {
255 auto const r = wsc->findMsg(1s, [&](auto const& jv) {
256 return jv[jss::ledger_index] == 4;
257 });
258 if (!r)
259 break;
260
261 if (!r->isMember(jss::transaction))
262 continue;
263
264 BEAST_EXPECT(checkDeliveredAmount.checkTxn(
265 (*r)[jss::transaction], (*r)[jss::meta]));
266 }
267 }
268 BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
269 }
270 }
271 void
273 {
274 testcase("Ledger Request RPC DeliveredAmount");
275
276 using namespace test::jtx;
277 using namespace std::chrono_literals;
278
279 Account const alice("alice");
280 Account const bob("bob");
281 Account const carol("carol");
282 auto const gw = Account("gateway");
283 auto const USD = gw["USD"];
284
285 for (bool const afterSwitchTime : {true, false})
286 {
287 auto cfg = envconfig();
288 cfg->FEES.reference_fee = 10;
289 Env env(*this, std::move(cfg));
290 env.fund(XRP(10000), alice, bob, carol, gw);
291 env.trust(USD(1000), alice, bob, carol);
292 if (afterSwitchTime)
293 env.close(NetClock::time_point{446000000s});
294 else
295 env.close();
296
297 CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
298 // normal payments
299 env(pay(gw, alice, USD(50)));
300 checkDeliveredAmount.adjCountersSuccess();
301 env(pay(gw, alice, XRP(50)));
302 checkDeliveredAmount.adjCountersSuccess();
303
304 // partial payment
305 env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
306 checkDeliveredAmount.adjCountersPartialPayment();
307 env.require(balance(bob, USD(1000)));
308
309 // failed payment
310 env(pay(gw, carol, USD(9999999)), ter(tecPATH_PARTIAL));
311 checkDeliveredAmount.adjCountersFail();
312 env.require(balance(carol, USD(0)));
313
314 env.close();
315 std::string index;
316 Json::Value jvParams;
317 jvParams[jss::ledger_index] = 4u;
318 jvParams[jss::transactions] = true;
319 jvParams[jss::expand] = true;
320 auto const jtxn = env.rpc(
321 "json",
322 "ledger",
323 to_string(
324 jvParams))[jss::result][jss::ledger][jss::transactions];
325 for (auto const& t : jtxn)
326 BEAST_EXPECT(
327 checkDeliveredAmount.checkTxn(t, t[jss::metaData]));
328 BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
329 }
330 }
331
332public:
333 void
334 run() override
335 {
338 }
339};
340
341BEAST_DEFINE_TESTSUITE(DeliveredAmount, app, ripple);
342
343} // namespace test
344} // namespace ripple
Represents a JSON value.
Definition: json_value.h:150
bool isMember(char const *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:962
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
virtual Config & config()=0
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:39
AccountID id() const
Returns the Account ID.
Definition: Account.h:107
A transaction testing environment.
Definition: Env.h:121
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:535
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:264
Application & app()
Definition: Env.h:261
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:770
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
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:35
Set the flags on a JTx.
Definition: txflags.h:31
@ arrayValue
array value (ordered list)
Definition: json_value.h:45
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:30
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
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:302
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:114
constexpr std::uint32_t tfPartialPayment
Definition: TxFlags.h:105
@ tecPATH_PARTIAL
Definition: TER.h:282
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630