xrpld
Loading...
Searching...
No Matches
PayChan_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/acctdelete.h>
6#include <test/jtx/amount.h>
7#include <test/jtx/balance.h> // IWYU pragma: keep
8#include <test/jtx/credentials.h>
9#include <test/jtx/deposit.h>
10#include <test/jtx/fee.h>
11#include <test/jtx/flags.h>
12#include <test/jtx/ter.h>
13#include <test/jtx/ticket.h>
14#include <test/jtx/txflags.h>
15
16#include <xrpld/rpc/Context.h>
17#include <xrpld/rpc/Role.h>
18#include <xrpld/rpc/handlers/Handlers.h>
19
20#include <xrpl/basics/Buffer.h>
21#include <xrpl/basics/Slice.h>
22#include <xrpl/basics/base_uint.h>
23#include <xrpl/basics/chrono.h>
24#include <xrpl/basics/strHex.h>
25#include <xrpl/beast/unit_test/suite.h>
26#include <xrpl/beast/utility/Zero.h>
27#include <xrpl/core/Job.h>
28#include <xrpl/core/ServiceRegistry.h>
29#include <xrpl/json/json_value.h>
30#include <xrpl/json/to_string.h>
31#include <xrpl/ledger/Dir.h>
32#include <xrpl/ledger/ReadView.h>
33#include <xrpl/protocol/ApiVersion.h>
34#include <xrpl/protocol/Feature.h>
35#include <xrpl/protocol/Indexes.h>
36#include <xrpl/protocol/KeyType.h>
37#include <xrpl/protocol/LedgerFormats.h>
38#include <xrpl/protocol/PayChan.h>
39#include <xrpl/protocol/PublicKey.h>
40#include <xrpl/protocol/SField.h>
41#include <xrpl/protocol/STAmount.h>
42#include <xrpl/protocol/SecretKey.h>
43#include <xrpl/protocol/SeqProxy.h>
44#include <xrpl/protocol/Serializer.h>
45#include <xrpl/protocol/TER.h>
46#include <xrpl/protocol/TxFlags.h>
47#include <xrpl/protocol/jss.h>
48#include <xrpl/resource/Charge.h>
49#include <xrpl/resource/Consumer.h>
50#include <xrpl/resource/Fees.h>
51
52#include <algorithm>
53#include <cassert>
54#include <cstddef>
55#include <cstdint>
56#include <iterator>
57#include <optional>
58#include <set>
59#include <string>
60#include <string_view>
61#include <utility>
62#include <vector>
63
64namespace xrpl::test {
65using namespace jtx::paychan;
66
68{
70 channelKeyAndSle(ReadView const& view, jtx::Account const& account, jtx::Account const& dst)
71 {
72 auto const sle = view.read(keylet::account(account));
73 if (!sle)
74 return {};
75 auto const k =
76 keylet::payChannel(account, dst, SeqProxy::rawSequence((*sle)[sfSequence] - 1));
77 return {k.key, view.read(k)};
78 }
79
80 static Buffer
82 PublicKey const& pk,
83 SecretKey const& sk,
84 UInt256 const& channel,
85 STAmount const& authAmt)
86 {
87 Serializer msg;
89 return sign(pk, sk, msg.slice());
90 }
91
92 static STAmount
93 channelAmount(ReadView const& view, UInt256 const& chan)
94 {
95 auto const slep = view.read({ltPAYCHAN, chan});
96 if (!slep)
97 return XRPAmount{-1};
98 return (*slep)[sfAmount];
99 }
100
102 channelExpiration(ReadView const& view, UInt256 const& chan)
103 {
104 auto const slep = view.read({ltPAYCHAN, chan});
105 if (!slep)
106 return std::nullopt;
107 if (auto const r = (*slep)[~sfExpiration])
108 return r.value();
109 return std::nullopt;
110 }
111
112 void
114 {
115 testcase("simple");
116 using namespace jtx;
117 using namespace std::literals::chrono_literals;
118 Env env{*this, features};
119 auto const alice = Account("alice");
120 auto const bob = Account("bob");
121 auto usda = alice["USD"];
122 env.fund(XRP(10000), alice, bob);
123 auto const pk = alice.pk();
124 auto const settleDelay = 100s;
125 auto const chan = channel(alice, bob, env.seq(alice));
126 env(create(alice, bob, XRP(1000), settleDelay, pk));
127 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
128 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
129
130 {
131 auto const preAlice = env.balance(alice);
132 env(fund(alice, chan, XRP(1000)));
133 auto const feeDrops = env.current()->fees().base;
134 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
135 }
136
137 auto chanBal = channelBalance(*env.current(), chan);
138 auto chanAmt = channelAmount(*env.current(), chan);
139 BEAST_EXPECT(chanBal == XRP(0));
140 BEAST_EXPECT(chanAmt == XRP(2000));
141
142 {
143 // bad amounts (non-xrp, negative amounts)
144 env(create(alice, bob, usda(1000), settleDelay, pk), Ter(temBAD_AMOUNT));
145 env(fund(alice, chan, usda(1000)), Ter(temBAD_AMOUNT));
146 env(create(alice, bob, XRP(-1000), settleDelay, pk), Ter(temBAD_AMOUNT));
147 env(fund(alice, chan, XRP(-1000)), Ter(temBAD_AMOUNT));
148 }
149
150 // invalid account
151 env(create(alice, "noAccount", XRP(1000), settleDelay, pk), Ter(tecNO_DST));
152 // can't create channel to the same account
153 env(create(alice, alice, XRP(1000), settleDelay, pk), Ter(temDST_IS_SRC));
154 // invalid channel
155
156 env(fund(alice, channel(alice, "noAccount", env.seq(alice) - 1), XRP(1000)),
158 // not enough funds
159 env(create(alice, bob, XRP(10000), settleDelay, pk), Ter(tecUNFUNDED));
160
161 {
162 // No signature claim with bad amounts (negative and non-xrp)
163 auto const iou = usda(100).value();
164 auto const negXRP = XRP(-100).value();
165 auto const posXRP = XRP(100).value();
166 env(claim(alice, chan, iou, iou), Ter(temBAD_AMOUNT));
167 env(claim(alice, chan, posXRP, iou), Ter(temBAD_AMOUNT));
168 env(claim(alice, chan, iou, posXRP), Ter(temBAD_AMOUNT));
169 env(claim(alice, chan, negXRP, negXRP), Ter(temBAD_AMOUNT));
170 env(claim(alice, chan, posXRP, negXRP), Ter(temBAD_AMOUNT));
171 env(claim(alice, chan, negXRP, posXRP), Ter(temBAD_AMOUNT));
172 }
173 {
174 // No signature claim more than authorized
175 auto const delta = XRP(500);
176 auto const reqBal = chanBal + delta;
177 auto const authAmt = reqBal + XRP(-100);
178 assert(reqBal <= chanAmt);
179 env(claim(alice, chan, reqBal, authAmt), Ter(temBAD_AMOUNT));
180 }
181 {
182 // No signature needed since the owner is claiming
183 auto const preBob = env.balance(bob);
184 auto const delta = XRP(500);
185 auto const reqBal = chanBal + delta;
186 auto const authAmt = reqBal + XRP(100);
187 assert(reqBal <= chanAmt);
188 env(claim(alice, chan, reqBal, authAmt));
189 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
190 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
191 BEAST_EXPECT(env.balance(bob) == preBob + delta);
192 chanBal = reqBal;
193 }
194 {
195 // Claim with signature
196 auto preBob = env.balance(bob);
197 auto const delta = XRP(500);
198 auto const reqBal = chanBal + delta;
199 auto const authAmt = reqBal + XRP(100);
200 assert(reqBal <= chanAmt);
201 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
202 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
203 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
204 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
205 auto const feeDrops = env.current()->fees().base;
206 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
207 chanBal = reqBal;
208
209 // claim again
210 preBob = env.balance(bob);
211 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
213 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
214 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
215 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
216 }
217 {
218 // Try to claim more than authorized
219 auto const preBob = env.balance(bob);
220 STAmount const authAmt = chanBal + XRP(500);
221 STAmount const reqAmt = authAmt + STAmount{1};
222 assert(reqAmt <= chanAmt);
223 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
224 env(claim(bob, chan, reqAmt, authAmt, Slice(sig), alice.pk()), Ter(temBAD_AMOUNT));
225 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
226 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
227 BEAST_EXPECT(env.balance(bob) == preBob);
228 }
229
230 // Dst tries to fund the channel
231 env(fund(bob, chan, XRP(1000)), Ter(tecNO_PERMISSION));
232 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
233 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
234
235 {
236 // Wrong signing key
237 auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500));
238 env(claim(bob, chan, XRP(1500).value(), XRP(1500).value(), Slice(sig), bob.pk()),
240 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
241 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
242 }
243 {
244 // Bad signature
245 auto const sig = signClaimAuth(bob.pk(), bob.sk(), chan, XRP(1500));
246 env(claim(bob, chan, XRP(1500).value(), XRP(1500).value(), Slice(sig), alice.pk()),
248 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
249 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
250 }
251 {
252 // Dst closes channel
253 auto const preAlice = env.balance(alice);
254 auto const preBob = env.balance(bob);
255 env(claim(bob, chan), Txflags(tfClose));
256 BEAST_EXPECT(!channelExists(*env.current(), chan));
257 auto const feeDrops = env.current()->fees().base;
258 auto const delta = chanAmt - chanBal;
259 assert(delta > beast::kZero);
260 BEAST_EXPECT(env.balance(alice) == preAlice + delta);
261 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
262 }
263 }
264
265 void
267 {
268 testcase("Disallow Incoming Flag");
269 using namespace jtx;
270
271 using namespace std::literals::chrono_literals;
272 Env env{*this, features};
273 auto const alice = Account("alice");
274 auto const bob = Account("bob");
275 auto const cho = Account("cho");
276 env.fund(XRP(10000), alice, bob, cho);
277 auto const pk = alice.pk();
278 auto const settleDelay = 100s;
279
280 // set flag on bob only
281 env(fset(bob, asfDisallowIncomingPayChan));
282 env.close();
283
284 // channel creation from alice to bob is disallowed
285 {
286 auto const chan = channel(alice, bob, env.seq(alice));
287 env(create(alice, bob, XRP(1000), settleDelay, pk), Ter(tecNO_PERMISSION));
288 BEAST_EXPECT(!channelExists(*env.current(), chan));
289 }
290
291 // set flag on alice also
292 env(fset(alice, asfDisallowIncomingPayChan));
293 env.close();
294
295 // channel creation from bob to alice is now disallowed
296 {
297 auto const chan = channel(bob, alice, env.seq(bob));
298 env(create(bob, alice, XRP(1000), settleDelay, pk), Ter(tecNO_PERMISSION));
299 BEAST_EXPECT(!channelExists(*env.current(), chan));
300 }
301
302 // remove flag from bob
303 env(fclear(bob, asfDisallowIncomingPayChan));
304 env.close();
305
306 // now the channel between alice and bob can exist
307 {
308 auto const chan = channel(alice, bob, env.seq(alice));
309 env(create(alice, bob, XRP(1000), settleDelay, pk), Ter(tesSUCCESS));
310 BEAST_EXPECT(channelExists(*env.current(), chan));
311 }
312
313 // a channel from cho to alice isn't allowed
314 {
315 auto const chan = channel(cho, alice, env.seq(cho));
316 env(create(cho, alice, XRP(1000), settleDelay, pk), Ter(tecNO_PERMISSION));
317 BEAST_EXPECT(!channelExists(*env.current(), chan));
318 }
319
320 // remove flag from alice
321 env(fclear(alice, asfDisallowIncomingPayChan));
322 env.close();
323
324 // now a channel from cho to alice is allowed
325 {
326 auto const chan = channel(cho, alice, env.seq(cho));
327 env(create(cho, alice, XRP(1000), settleDelay, pk), Ter(tesSUCCESS));
328 BEAST_EXPECT(channelExists(*env.current(), chan));
329 }
330 }
331
332 void
334 {
335 testcase("cancel after");
336 using namespace jtx;
337 using namespace std::literals::chrono_literals;
338 auto const alice = Account("alice");
339 auto const bob = Account("bob");
340 auto const carol = Account("carol");
341 {
342 // If dst claims after cancel after, channel closes
343 Env env{*this, features};
344 env.fund(XRP(10000), alice, bob);
345 auto const pk = alice.pk();
346 auto const settleDelay = 100s;
347 NetClock::time_point const cancelAfter =
348 env.current()->header().parentCloseTime + 3600s;
349 auto const channelFunds = XRP(1000);
350 auto const chan = channel(alice, bob, env.seq(alice));
351 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
352 BEAST_EXPECT(channelExists(*env.current(), chan));
353 env.close(cancelAfter);
354 {
355 // dst cannot claim after cancelAfter
356 auto const chanBal = channelBalance(*env.current(), chan);
357 auto const chanAmt = channelAmount(*env.current(), chan);
358 auto preAlice = env.balance(alice);
359 auto preBob = env.balance(bob);
360 auto const delta = XRP(500);
361 auto const reqBal = chanBal + delta;
362 auto const authAmt = reqBal + XRP(100);
363 assert(reqBal <= chanAmt);
364 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
365 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
366 auto const feeDrops = env.current()->fees().base;
367 BEAST_EXPECT(!channelExists(*env.current(), chan));
368 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
369 BEAST_EXPECT(env.balance(alice) == preAlice + channelFunds);
370 }
371 }
372 {
373 // Third party can close after cancel after
374 Env env{*this, features};
375 env.fund(XRP(10000), alice, bob, carol);
376 auto const pk = alice.pk();
377 auto const settleDelay = 100s;
378 NetClock::time_point const cancelAfter =
379 env.current()->header().parentCloseTime + 3600s;
380 auto const channelFunds = XRP(1000);
381 auto const chan = channel(alice, bob, env.seq(alice));
382 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
383 BEAST_EXPECT(channelExists(*env.current(), chan));
384 // third party close before cancelAfter
385 env(claim(carol, chan), Txflags(tfClose), Ter(tecNO_PERMISSION));
386 BEAST_EXPECT(channelExists(*env.current(), chan));
387 env.close(cancelAfter);
388 // third party close after cancelAfter
389 auto const preAlice = env.balance(alice);
390 env(claim(carol, chan), Txflags(tfClose));
391 BEAST_EXPECT(!channelExists(*env.current(), chan));
392 BEAST_EXPECT(env.balance(alice) == preAlice + channelFunds);
393 }
394 // fixPayChanCancelAfter
395 // CancelAfter should be greater than close time
396 {
397 for (bool const withFixPayChan : {true, false})
398 {
399 auto const amend = withFixPayChan ? features : features - fixPayChanCancelAfter;
400 Env env{*this, amend};
401 env.fund(XRP(10000), alice, bob);
402 env.close();
403
404 auto const pk = alice.pk();
405 auto const settleDelay = 100s;
406 auto const channelFunds = XRP(1000);
407 NetClock::time_point const cancelAfter =
408 env.current()->header().parentCloseTime - 1s;
409 auto const txResult = withFixPayChan ? Ter(tecEXPIRED) : Ter(tesSUCCESS);
410 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter), txResult);
411 }
412 }
413 // fixPayChanCancelAfter
414 // CancelAfter can be equal to the close time
415 {
416 for (bool const withFixPayChan : {true, false})
417 {
418 auto const amend = withFixPayChan ? features : features - fixPayChanCancelAfter;
419 Env env{*this, amend};
420 env.fund(XRP(10000), alice, bob);
421 env.close();
422
423 auto const pk = alice.pk();
424 auto const settleDelay = 100s;
425 auto const channelFunds = XRP(1000);
426 NetClock::time_point const cancelAfter = env.current()->header().parentCloseTime;
427 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter),
428 Ter(tesSUCCESS));
429 }
430 }
431 }
432
433 void
435 {
436 testcase("expiration");
437 using namespace jtx;
438 using namespace std::literals::chrono_literals;
439 Env env{*this, features};
440 auto const alice = Account("alice");
441 auto const bob = Account("bob");
442 auto const carol = Account("carol");
443 env.fund(XRP(10000), alice, bob, carol);
444 auto const pk = alice.pk();
445 auto const settleDelay = 3600s;
446 auto const closeTime = env.current()->header().parentCloseTime;
447 auto const minExpiration = closeTime + settleDelay;
448 NetClock::time_point const cancelAfter = closeTime + 7200s;
449 auto const channelFunds = XRP(1000);
450 auto const chan = channel(alice, bob, env.seq(alice));
451 env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
452 BEAST_EXPECT(channelExists(*env.current(), chan));
453 BEAST_EXPECT(!channelExpiration(*env.current(), chan));
454 // Owner closes, will close after settleDelay
455 env(claim(alice, chan), Txflags(tfClose));
456 auto counts = [](auto const& t) { return t.time_since_epoch().count(); };
457 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
458 BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration));
459 // increase the expiration time
460 env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 100s}));
461 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
462 BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 100);
463 // decrease the expiration, but still above minExpiration
464 env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 50s}));
465 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
466 BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50);
467 // decrease the expiration below minExpiration
468 env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}),
470 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
471 BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50);
472 env(claim(bob, chan), Txflags(tfRenew), Ter(tecNO_PERMISSION));
473 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
474 BEAST_EXPECT(*channelExpiration(*env.current(), chan) == counts(minExpiration) + 50);
475 env(claim(alice, chan), Txflags(tfRenew));
476 BEAST_EXPECT(!channelExpiration(*env.current(), chan));
477 // decrease the expiration below minExpiration
478 env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}),
480 BEAST_EXPECT(!channelExpiration(*env.current(), chan));
481 env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration}));
482 env.close(minExpiration);
483 // Try to extend the expiration after the expiration has already passed
484 env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 1000s}));
485 BEAST_EXPECT(!channelExists(*env.current(), chan));
486 }
487
488 void
490 {
491 testcase("settle delay");
492 using namespace jtx;
493 using namespace std::literals::chrono_literals;
494 Env env{*this, features};
495 auto const alice = Account("alice");
496 auto const bob = Account("bob");
497 env.fund(XRP(10000), alice, bob);
498 auto const pk = alice.pk();
499 auto const settleDelay = 3600s;
500 NetClock::time_point const settleTimepoint =
501 env.current()->header().parentCloseTime + settleDelay;
502 auto const channelFunds = XRP(1000);
503 auto const chan = channel(alice, bob, env.seq(alice));
504 env(create(alice, bob, channelFunds, settleDelay, pk));
505 BEAST_EXPECT(channelExists(*env.current(), chan));
506 // Owner closes, will close after settleDelay
507 env(claim(alice, chan), Txflags(tfClose));
508 BEAST_EXPECT(channelExists(*env.current(), chan));
509 env.close(settleTimepoint - (settleDelay / 2));
510 {
511 // receiver can still claim
512 auto const chanBal = channelBalance(*env.current(), chan);
513 auto const chanAmt = channelAmount(*env.current(), chan);
514 auto preBob = env.balance(bob);
515 auto const delta = XRP(500);
516 auto const reqBal = chanBal + delta;
517 auto const authAmt = reqBal + XRP(100);
518 assert(reqBal <= chanAmt);
519 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
520 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
521 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
522 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
523 auto const feeDrops = env.current()->fees().base;
524 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
525 }
526 env.close(settleTimepoint);
527 {
528 // past settleTime, channel will close
529 auto const chanBal = channelBalance(*env.current(), chan);
530 auto const chanAmt = channelAmount(*env.current(), chan);
531 auto const preAlice = env.balance(alice);
532 auto preBob = env.balance(bob);
533 auto const delta = XRP(500);
534 auto const reqBal = chanBal + delta;
535 auto const authAmt = reqBal + XRP(100);
536 assert(reqBal <= chanAmt);
537 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
538 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
539 BEAST_EXPECT(!channelExists(*env.current(), chan));
540 auto const feeDrops = env.current()->fees().base;
541 BEAST_EXPECT(env.balance(alice) == preAlice + chanAmt - chanBal);
542 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
543 }
544 }
545
546 void
548 {
549 testcase("close dry");
550 using namespace jtx;
551 using namespace std::literals::chrono_literals;
552 Env env{*this, features};
553 auto const alice = Account("alice");
554 auto const bob = Account("bob");
555 env.fund(XRP(10000), alice, bob);
556 auto const pk = alice.pk();
557 auto const settleDelay = 3600s;
558 auto const channelFunds = XRP(1000);
559 auto const chan = channel(alice, bob, env.seq(alice));
560 env(create(alice, bob, channelFunds, settleDelay, pk));
561 BEAST_EXPECT(channelExists(*env.current(), chan));
562 // Owner tries to close channel, but it will remain open (settle delay)
563 env(claim(alice, chan), Txflags(tfClose));
564 BEAST_EXPECT(channelExists(*env.current(), chan));
565 {
566 // claim the entire amount
567 auto const preBob = env.balance(bob);
568 env(claim(alice, chan, channelFunds.value(), channelFunds.value()));
569 BEAST_EXPECT(channelBalance(*env.current(), chan) == channelFunds);
570 BEAST_EXPECT(env.balance(bob) == preBob + channelFunds);
571 }
572 auto const preAlice = env.balance(alice);
573 // Channel is now dry, can close before expiration date
574 env(claim(alice, chan), Txflags(tfClose));
575 BEAST_EXPECT(!channelExists(*env.current(), chan));
576 auto const feeDrops = env.current()->fees().base;
577 BEAST_EXPECT(env.balance(alice) == preAlice - feeDrops);
578 }
579
580 void
582 {
583 // auth amount defaults to balance if not present
584 testcase("default amount");
585 using namespace jtx;
586 using namespace std::literals::chrono_literals;
587 Env env{*this, features};
588 auto const alice = Account("alice");
589 auto const bob = Account("bob");
590 env.fund(XRP(10000), alice, bob);
591 auto const pk = alice.pk();
592 auto const settleDelay = 3600s;
593 auto const channelFunds = XRP(1000);
594 auto const chan = channel(alice, bob, env.seq(alice));
595 env(create(alice, bob, channelFunds, settleDelay, pk));
596 BEAST_EXPECT(channelExists(*env.current(), chan));
597 // Owner tries to close channel, but it will remain open (settle delay)
598 env(claim(alice, chan), Txflags(tfClose));
599 BEAST_EXPECT(channelExists(*env.current(), chan));
600 {
601 auto chanBal = channelBalance(*env.current(), chan);
602 auto chanAmt = channelAmount(*env.current(), chan);
603 auto const preBob = env.balance(bob);
604
605 auto const delta = XRP(500);
606 auto const reqBal = chanBal + delta;
607 assert(reqBal <= chanAmt);
608 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, reqBal);
609 env(claim(bob, chan, reqBal, std::nullopt, Slice(sig), alice.pk()));
610 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
611 auto const feeDrops = env.current()->fees().base;
612 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
613 chanBal = reqBal;
614 }
615 {
616 // Claim again
617 auto chanBal = channelBalance(*env.current(), chan);
618 auto chanAmt = channelAmount(*env.current(), chan);
619 auto const preBob = env.balance(bob);
620
621 auto const delta = XRP(500);
622 auto const reqBal = chanBal + delta;
623 assert(reqBal <= chanAmt);
624 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, reqBal);
625 env(claim(bob, chan, reqBal, std::nullopt, Slice(sig), alice.pk()));
626 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
627 auto const feeDrops = env.current()->fees().base;
628 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
629 chanBal = reqBal;
630 }
631 }
632
633 void
635 {
636 // auth amount defaults to balance if not present
637 testcase("Disallow XRP");
638 using namespace jtx;
639 using namespace std::literals::chrono_literals;
640
641 auto const alice = Account("alice");
642 auto const bob = Account("bob");
643 {
644 // Create a channel where dst disallows XRP. Ignore that flag,
645 // since it's just advisory.
646 Env env{*this, features};
647 env.fund(XRP(10000), alice, bob);
648 env(fset(bob, asfDisallowXRP));
649 auto const chan = channel(alice, bob, env.seq(alice));
650 env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
651 BEAST_EXPECT(channelExists(*env.current(), chan));
652 }
653
654 {
655 // Claim to a channel where dst disallows XRP (channel is
656 // created before disallow xrp is set). Ignore that flag
657 // since it is just advisory.
658 Env env{*this, features};
659 env.fund(XRP(10000), alice, bob);
660 auto const chan = channel(alice, bob, env.seq(alice));
661 env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
662 BEAST_EXPECT(channelExists(*env.current(), chan));
663
664 env(fset(bob, asfDisallowXRP));
665 auto const reqBal = XRP(500).value();
666 env(claim(alice, chan, reqBal, reqBal));
667 }
668 }
669
670 void
672 {
673 // auth amount defaults to balance if not present
674 testcase("Dst Tag");
675 using namespace jtx;
676 using namespace std::literals::chrono_literals;
677 // Create a channel where dst disallows XRP
678 Env env{*this, features};
679 auto const alice = Account("alice");
680 auto const bob = Account("bob");
681 env.fund(XRP(10000), alice, bob);
682 env(fset(bob, asfRequireDest));
683 auto const pk = alice.pk();
684 auto const settleDelay = 3600s;
685 auto const channelFunds = XRP(1000);
686 {
687 auto const chan = channel(alice, bob, env.seq(alice));
688 env(create(alice, bob, channelFunds, settleDelay, pk), Ter(tecDST_TAG_NEEDED));
689 BEAST_EXPECT(!channelExists(*env.current(), chan));
690 }
691 {
692 auto const chan = channel(alice, bob, env.seq(alice));
693 env(create(alice, bob, channelFunds, settleDelay, pk, std::nullopt, 1));
694 BEAST_EXPECT(channelExists(*env.current(), chan));
695 }
696 }
697
698 void
700 {
701 testcase("Deposit Authorization");
702 using namespace jtx;
703 using namespace std::literals::chrono_literals;
704
705 auto const alice = Account("alice");
706 auto const bob = Account("bob");
707 auto const carol = Account("carol");
708 auto usda = alice["USD"];
709 {
710 Env env{*this, features};
711 env.fund(XRP(10000), alice, bob, carol);
712
713 env(fset(bob, asfDepositAuth));
714 env.close();
715
716 auto const pk = alice.pk();
717 auto const settleDelay = 100s;
718 auto const chan = channel(alice, bob, env.seq(alice));
719 env(create(alice, bob, XRP(1000), settleDelay, pk));
720 env.close();
721
722 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
723 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
724
725 // alice can add more funds to the channel even though bob has
726 // asfDepositAuth set.
727 env(fund(alice, chan, XRP(1000)));
728 env.close();
729
730 // alice claims. Fails because bob's lsfDepositAuth flag is set.
731 env(claim(alice, chan, XRP(500).value(), XRP(500).value()), Ter(tecNO_PERMISSION));
732 env.close();
733
734 // Claim with signature
735 auto const baseFee = env.current()->fees().base;
736 auto const preBob = env.balance(bob);
737 {
738 auto const delta = XRP(500).value();
739 auto const sig = signClaimAuth(pk, alice.sk(), chan, delta);
740
741 // alice claims with signature. Fails since bob has
742 // lsfDepositAuth flag set.
743 env(claim(alice, chan, delta, delta, Slice(sig), pk), Ter(tecNO_PERMISSION));
744 env.close();
745 BEAST_EXPECT(env.balance(bob) == preBob);
746
747 // bob claims but omits the signature. Fails because only
748 // alice can claim without a signature.
749 env(claim(bob, chan, delta, delta), Ter(temBAD_SIGNATURE));
750 env.close();
751
752 // bob claims with signature. Succeeds even though bob's
753 // lsfDepositAuth flag is set since bob submitted the
754 // transaction.
755 env(claim(bob, chan, delta, delta, Slice(sig), pk));
756 env.close();
757 BEAST_EXPECT(env.balance(bob) == preBob + delta - baseFee);
758 }
759 {
760 // Explore the limits of deposit pre-authorization.
761 auto const delta = XRP(600).value();
762 auto const sig = signClaimAuth(pk, alice.sk(), chan, delta);
763
764 // carol claims and fails. Only channel participants (bob or
765 // alice) may claim.
766 env(claim(carol, chan, delta, delta, Slice(sig), pk), Ter(tecNO_PERMISSION));
767 env.close();
768
769 // bob preauthorizes carol for deposit. But after that carol
770 // still can't claim since only channel participants may claim.
771 env(deposit::auth(bob, carol));
772 env.close();
773
774 env(claim(carol, chan, delta, delta, Slice(sig), pk), Ter(tecNO_PERMISSION));
775
776 // Since alice is not preauthorized she also may not claim
777 // for bob.
778 env(claim(alice, chan, delta, delta, Slice(sig), pk), Ter(tecNO_PERMISSION));
779 env.close();
780
781 // However if bob preauthorizes alice for deposit then she can
782 // successfully submit a claim.
783 env(deposit::auth(bob, alice));
784 env.close();
785
786 env(claim(alice, chan, delta, delta, Slice(sig), pk));
787 env.close();
788
789 BEAST_EXPECT(env.balance(bob) == preBob + delta - (3 * baseFee));
790 }
791 {
792 // bob removes pre-authorization of alice. Once again she
793 // cannot submit a claim.
794 auto const delta = XRP(800).value();
795
796 env(deposit::unauth(bob, alice));
797 env.close();
798
799 // alice claims and fails since she is no longer preauthorized.
800 env(claim(alice, chan, delta, delta), Ter(tecNO_PERMISSION));
801 env.close();
802
803 // bob clears lsfDepositAuth. Now alice can claim.
804 env(fclear(bob, asfDepositAuth));
805 env.close();
806
807 // alice claims successfully.
808 env(claim(alice, chan, delta, delta));
809 env.close();
810 BEAST_EXPECT(env.balance(bob) == preBob + XRP(800) - (5 * baseFee));
811 }
812 }
813 }
814
815 void
817 {
818 testcase("Deposit Authorization with Credentials");
819 using namespace jtx;
820 using namespace std::literals::chrono_literals;
821
822 char const credType[] = "abcde";
823
824 Account const alice("alice");
825 Account const bob("bob");
826 Account const carol("carol");
827 Account const dillon("dillon");
828 Account const zelda("zelda");
829
830 {
831 Env env{*this};
832 env.fund(XRP(10000), alice, bob, carol, dillon, zelda);
833
834 auto const pk = alice.pk();
835 auto const settleDelay = 100s;
836 auto const chan = channel(alice, bob, env.seq(alice));
837 env(create(alice, bob, XRP(1000), settleDelay, pk));
838 env.close();
839
840 // alice add funds to the channel
841 env(fund(alice, chan, XRP(1000)));
842 env.close();
843
844 std::string const credBadIdx =
845 "D007AE4B6E1274B4AF872588267B810C2F82716726351D1C7D38D3E5499FC6"
846 "E1";
847
848 auto const delta = XRP(500).value();
849
850 { // create credentials
851 auto jv = credentials::create(alice, carol, credType);
852 uint32_t const t =
853 env.current()->header().parentCloseTime.time_since_epoch().count() + 100;
854 jv[sfExpiration.jsonName] = t;
855 env(jv);
856 env.close();
857 }
858
859 auto const jv = credentials::ledgerEntry(env, alice, carol, credType);
860 std::string const credIdx = jv[jss::result][jss::index].asString();
861
862 // Bob require pre-authorization
863 env(fset(bob, asfDepositAuth));
864 env.close();
865
866 // Fail, credentials not accepted
867 env(claim(alice, chan, delta, delta),
868 credentials::Ids({credIdx}),
870 env.close();
871
872 env(credentials::accept(alice, carol, credType));
873 env.close();
874
875 // Fail, no depositPreauth object
876 env(claim(alice, chan, delta, delta),
877 credentials::Ids({credIdx}),
879 env.close();
880
881 // Setup deposit authorization
882 env(deposit::authCredentials(bob, {{.issuer = carol, .credType = credType}}));
883 env.close();
884
885 // Fail, credentials doesn’t belong to root account
886 env(claim(dillon, chan, delta, delta),
887 credentials::Ids({credIdx}),
889
890 // Fails because bob's lsfDepositAuth flag is set.
891 env(claim(alice, chan, delta, delta), Ter(tecNO_PERMISSION));
892
893 // Fail, bad credentials index.
894 env(claim(alice, chan, delta, delta),
895 credentials::Ids({credBadIdx}),
897
898 // Fail, empty credentials
899 env(claim(alice, chan, delta, delta), credentials::Ids({}), Ter(temMALFORMED));
900
901 {
902 // claim fails cause of expired credentials
903
904 // Every cycle +10sec.
905 for (int i = 0; i < 10; ++i)
906 env.close();
907
908 env(claim(alice, chan, delta, delta), credentials::Ids({credIdx}), Ter(tecEXPIRED));
909 env.close();
910 }
911
912 { // create credentials once more
913 env(credentials::create(alice, carol, credType));
914 env.close();
915 env(credentials::accept(alice, carol, credType));
916 env.close();
917
918 auto const jv = credentials::ledgerEntry(env, alice, carol, credType);
919 std::string const credIdx = jv[jss::result][jss::index].asString();
920
921 // Success
922 env(claim(alice, chan, delta, delta), credentials::Ids({credIdx}));
923 }
924 }
925
926 {
927 Env env{*this};
928 env.fund(XRP(10000), alice, bob, carol, dillon, zelda);
929
930 auto const pk = alice.pk();
931 auto const settleDelay = 100s;
932 auto const chan = channel(alice, bob, env.seq(alice));
933 env(create(alice, bob, XRP(1000), settleDelay, pk));
934 env.close();
935
936 // alice add funds to the channel
937 env(fund(alice, chan, XRP(1000)));
938 env.close();
939
940 auto const delta = XRP(500).value();
941
942 { // create credentials
943 env(credentials::create(alice, carol, credType));
944 env.close();
945 env(credentials::accept(alice, carol, credType));
946 env.close();
947 }
948
949 auto const jv = credentials::ledgerEntry(env, alice, carol, credType);
950 std::string const credIdx = jv[jss::result][jss::index].asString();
951
952 // Succeed, lsfDepositAuth is not set
953 env(claim(alice, chan, delta, delta), credentials::Ids({credIdx}));
954 env.close();
955 }
956
957 {
958 // Credentials amendment not enabled
959 Env env(*this, testableAmendments() - featureCredentials);
960 env.fund(XRP(5000), "alice", "bob");
961 env.close();
962
963 auto const pk = alice.pk();
964 auto const settleDelay = 100s;
965 auto const chan = channel(alice, bob, env.seq(alice));
966 env(create(alice, bob, XRP(1000), settleDelay, pk));
967 env.close();
968
969 env(fund(alice, chan, XRP(1000)));
970 env.close();
971 std::string const credIdx =
972 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
973 "E4";
974
975 // can't claim with old DepositPreauth because rule is not enabled.
976 env(fset(bob, asfDepositAuth));
977 env.close();
978 env(deposit::auth(bob, alice));
979 env.close();
980
981 env(claim(alice, chan, XRP(500).value(), XRP(500).value()),
982 credentials::Ids({credIdx}),
984 }
985 }
986
987 void
989 {
990 // auth amount defaults to balance if not present
991 testcase("Multiple channels to the same account");
992 using namespace jtx;
993 using namespace std::literals::chrono_literals;
994 Env env{*this, features};
995 auto const alice = Account("alice");
996 auto const bob = Account("bob");
997 env.fund(XRP(10000), alice, bob);
998 auto const pk = alice.pk();
999 auto const settleDelay = 3600s;
1000 auto const channelFunds = XRP(1000);
1001 auto const chan1 = channel(alice, bob, env.seq(alice));
1002 env(create(alice, bob, channelFunds, settleDelay, pk));
1003 BEAST_EXPECT(channelExists(*env.current(), chan1));
1004 auto const chan2 = channel(alice, bob, env.seq(alice));
1005 env(create(alice, bob, channelFunds, settleDelay, pk));
1006 BEAST_EXPECT(channelExists(*env.current(), chan2));
1007 BEAST_EXPECT(chan1 != chan2);
1008 }
1009
1010 void
1012 {
1013 testcase("AccountChannels RPC");
1014
1015 using namespace jtx;
1016 using namespace std::literals::chrono_literals;
1017 Env env{*this, features};
1018 auto const alice = Account("alice");
1019 auto const bob = Account("bob");
1020 auto const charlie = Account("charlie", KeyType::Ed25519);
1021 env.fund(XRP(10000), alice, bob, charlie);
1022 auto const pk = alice.pk();
1023 auto const settleDelay = 3600s;
1024 auto const channelFunds = XRP(1000);
1025 auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
1026 env(create(alice, bob, channelFunds, settleDelay, pk));
1027 env.close();
1028 {
1029 // test account non-string
1030 auto testInvalidAccountParam = [&](auto const& param) {
1031 json::Value params;
1032 params[jss::account] = param;
1033 auto const jrr =
1034 env.rpc("json", "account_channels", to_string(params))[jss::result];
1035 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1036 BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
1037 };
1038
1039 testInvalidAccountParam(1);
1040 testInvalidAccountParam(1.1);
1041 testInvalidAccountParam(true);
1042 testInvalidAccountParam(json::Value(json::ValueType::Null));
1043 testInvalidAccountParam(json::Value(json::ValueType::Object));
1044 testInvalidAccountParam(json::Value(json::ValueType::Array));
1045 }
1046 {
1047 // test destination_account non-string
1048 auto testInvalidDestAccountParam = [&](auto const& param) {
1049 json::Value params;
1050 params[jss::account] = alice.human();
1051 params[jss::destination_account] = param;
1052 auto const jrr =
1053 env.rpc("json", "account_channels", to_string(params))[jss::result];
1054 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
1055 BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'destination_account'.");
1056 };
1057
1058 testInvalidDestAccountParam(1);
1059 testInvalidDestAccountParam(1.1);
1060 testInvalidDestAccountParam(true);
1061 testInvalidDestAccountParam(json::Value(json::ValueType::Null));
1062 testInvalidDestAccountParam(json::Value(json::ValueType::Object));
1063 testInvalidDestAccountParam(json::Value(json::ValueType::Array));
1064 }
1065 {
1066 auto const r = env.rpc("account_channels", alice.human(), bob.human());
1067 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1068 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1069 BEAST_EXPECT(r[jss::result][jss::validated]);
1070 }
1071 {
1072 auto const r = env.rpc("account_channels", alice.human());
1073 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1074 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1075 BEAST_EXPECT(r[jss::result][jss::validated]);
1076 }
1077 {
1078 auto const r = env.rpc("account_channels", bob.human(), alice.human());
1079 BEAST_EXPECT(r[jss::result][jss::channels].size() == 0);
1080 BEAST_EXPECT(r[jss::result][jss::validated]);
1081 }
1082 auto const chan2Str = to_string(channel(alice, bob, env.seq(alice)));
1083 env(create(alice, bob, channelFunds, settleDelay, pk));
1084 env.close();
1085 {
1086 auto const r = env.rpc("account_channels", alice.human(), bob.human());
1087 BEAST_EXPECT(r[jss::result][jss::channels].size() == 2);
1088 BEAST_EXPECT(r[jss::result][jss::validated]);
1089 BEAST_EXPECT(chan1Str != chan2Str);
1090 for (auto const& c : {chan1Str, chan2Str})
1091 {
1092 BEAST_EXPECT(
1093 r[jss::result][jss::channels][0u][jss::channel_id] == c ||
1094 r[jss::result][jss::channels][1u][jss::channel_id] == c);
1095 }
1096 }
1097 }
1098
1099 void
1101 {
1102 testcase("Account channels RPC markers");
1103
1104 using namespace test::jtx;
1105 using namespace std::literals;
1106
1107 auto const alice = Account("alice");
1108 auto const bobs = []() -> std::vector<Account> {
1109 int const n = 10;
1111 r.reserve(n);
1112 for (int i = 0; i < n; ++i)
1113 {
1114 r.emplace_back("bob"s + std::to_string(i));
1115 }
1116 return r;
1117 }();
1118
1119 Env env{*this, features};
1120 env.fund(XRP(10000), alice);
1121 for (auto const& a : bobs)
1122 {
1123 env.fund(XRP(10000), a);
1124 env.close();
1125 }
1126
1127 {
1128 // create a channel from alice to every bob account
1129 auto const settleDelay = 3600s;
1130 auto const channelFunds = XRP(1);
1131 for (auto const& b : bobs)
1132 {
1133 env(create(alice, b, channelFunds, settleDelay, alice.pk()));
1134 }
1135 }
1136
1137 auto testLimit = [](test::jtx::Env& env,
1138 test::jtx::Account const& src,
1139 std::optional<int> limit = std::nullopt,
1140 json::Value const& marker = json::ValueType::Null,
1141 std::optional<test::jtx::Account> const& dst = std::nullopt) {
1142 json::Value jvc;
1143 jvc[jss::account] = src.human();
1144 if (dst)
1145 jvc[jss::destination_account] = dst->human();
1146 if (limit)
1147 jvc[jss::limit] = *limit;
1148 if (marker)
1149 jvc[jss::marker] = marker;
1150
1151 return env.rpc("json", "account_channels", to_string(jvc))[jss::result];
1152 };
1153
1154 {
1155 // No marker
1156 auto const r = testLimit(env, alice);
1157 BEAST_EXPECT(r.isMember(jss::channels));
1158 BEAST_EXPECT(r[jss::channels].size() == bobs.size());
1159 }
1160
1161 auto const bobsB58 = [&bobs]() -> std::set<std::string> {
1163 for (auto const& a : bobs)
1164 r.insert(a.human());
1165 return r;
1166 }();
1167
1168 for (int limit = 1; limit < bobs.size() + 1; ++limit)
1169 {
1170 auto leftToFind = bobsB58;
1171 auto const numFull = bobs.size() / limit;
1172 auto const numNonFull = ((bobs.size() % limit) != 0u) ? 1 : 0;
1173
1175
1176 auto const testIt = [&](bool expectMarker, int expectedBatchSize) {
1177 auto const r = testLimit(env, alice, limit, marker);
1178 BEAST_EXPECT(!expectMarker || r.isMember(jss::marker));
1179 if (r.isMember(jss::marker))
1180 marker = r[jss::marker];
1181 BEAST_EXPECT(r[jss::channels].size() == expectedBatchSize);
1182 auto const c = r[jss::channels];
1183 auto const s = r[jss::channels].size();
1184 for (int j = 0; j < s; ++j)
1185 {
1186 auto const dstAcc = c[j][jss::destination_account].asString();
1187 BEAST_EXPECT(leftToFind.count(dstAcc));
1188 leftToFind.erase(dstAcc);
1189 }
1190 };
1191
1192 for (int i = 0; i < numFull; ++i)
1193 {
1194 bool const expectMarker = (numNonFull != 0 || i < numFull - 1);
1195 testIt(expectMarker, limit);
1196 }
1197
1198 if (numNonFull != 0)
1199 {
1200 testIt(false, bobs.size() % limit);
1201 }
1202 BEAST_EXPECT(leftToFind.empty());
1203 }
1204
1205 {
1206 // degenerate case
1207 auto const r = testLimit(env, alice, 0);
1208 BEAST_EXPECT(r.isMember(jss::error_message));
1209 }
1210 }
1211
1212 void
1214 {
1215 // Check that the account_channels command only returns channels owned
1216 // by the account
1217 testcase("Account channels RPC owner only");
1218
1219 using namespace test::jtx;
1220 using namespace std::literals;
1221
1222 auto const alice = Account("alice");
1223 auto const bob = Account("bob");
1224 Env env{*this, features};
1225 env.fund(XRP(10000), alice, bob);
1226
1227 // Create a channel from alice to bob and from bob to alice
1228 // When retrieving alice's channels, it should only retrieve the
1229 // channels where alice is the source, not the destination
1230 auto const settleDelay = 3600s;
1231 auto const channelFunds = XRP(1000);
1232 env(create(alice, bob, channelFunds, settleDelay, alice.pk()));
1233 env(create(bob, alice, channelFunds, settleDelay, bob.pk()));
1234
1235 auto const r = [&] {
1236 json::Value jvc;
1237 jvc[jss::account] = alice.human();
1238
1239 return env.rpc("json", "account_channels", to_string(jvc))[jss::result];
1240 }();
1241 BEAST_EXPECT(r.isMember(jss::channels));
1242 BEAST_EXPECT(r[jss::channels].size() == 1);
1243 BEAST_EXPECT(r[jss::channels][0u][jss::destination_account].asString() == bob.human());
1244 }
1245
1246 void
1248 {
1249 using namespace jtx;
1250 using namespace std::literals::chrono_literals;
1251
1252 Env env{*this, features};
1253 auto const alice = Account("alice");
1254 auto const bob = Account("bob");
1255 auto const charlie = Account("charlie", KeyType::Ed25519);
1256 env.fund(XRP(10000), alice, bob, charlie);
1257 auto const pk = alice.pk();
1258 auto const settleDelay = 3600s;
1259 auto const channelFunds = XRP(1000);
1260 auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
1261 env(create(alice, bob, channelFunds, settleDelay, pk));
1262 env.close();
1263
1265 args[jss::channel_id] = chan1Str;
1266 args[jss::key_type] = "ed255191";
1267 args[jss::seed] = "snHq1rzQoN2qiUkC3XF5RyxBzUtN";
1268 args[jss::amount] = 51110000;
1269
1270 // test for all api versions
1271 forAllApiVersions([&, this](unsigned apiVersion) {
1272 testcase("PayChan Channel_Auth RPC Api " + std::to_string(apiVersion));
1273 args[jss::api_version] = apiVersion;
1274 auto const rs =
1275 env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1276 auto const error = apiVersion < 2u ? "invalidParams" : "badKeyType";
1277 BEAST_EXPECT(rs[jss::error] == error);
1278 });
1279 }
1280
1281 void
1283 {
1284 testcase("PayChan Auth/Verify RPC");
1285 using namespace jtx;
1286 using namespace std::literals::chrono_literals;
1287 Env env{*this, features};
1288 auto const alice = Account("alice");
1289 auto const bob = Account("bob");
1290 auto const charlie = Account("charlie", KeyType::Ed25519);
1291 env.fund(XRP(10000), alice, bob, charlie);
1292 auto const pk = alice.pk();
1293 auto const settleDelay = 3600s;
1294 auto const channelFunds = XRP(1000);
1295 auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
1296 env(create(alice, bob, channelFunds, settleDelay, pk));
1297 env.close();
1298 std::string chan1PkStr;
1299 {
1300 auto const r = env.rpc("account_channels", alice.human(), bob.human());
1301 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1302 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1303 BEAST_EXPECT(r[jss::result][jss::validated]);
1304 chan1PkStr = r[jss::result][jss::channels][0u][jss::public_key].asString();
1305 }
1306 {
1307 auto const r = env.rpc("account_channels", alice.human());
1308 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1309 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan1Str);
1310 BEAST_EXPECT(r[jss::result][jss::validated]);
1311 chan1PkStr = r[jss::result][jss::channels][0u][jss::public_key].asString();
1312 }
1313 {
1314 auto const r = env.rpc("account_channels", bob.human(), alice.human());
1315 BEAST_EXPECT(r[jss::result][jss::channels].size() == 0);
1316 BEAST_EXPECT(r[jss::result][jss::validated]);
1317 }
1318 auto const chan2Str = to_string(channel(alice, bob, env.seq(alice)));
1319 env(create(alice, bob, channelFunds, settleDelay, pk));
1320 env.close();
1321 {
1322 auto const r = env.rpc("account_channels", alice.human(), bob.human());
1323 BEAST_EXPECT(r[jss::result][jss::channels].size() == 2);
1324 BEAST_EXPECT(r[jss::result][jss::validated]);
1325 BEAST_EXPECT(chan1Str != chan2Str);
1326 for (auto const& c : {chan1Str, chan2Str})
1327 {
1328 BEAST_EXPECT(
1329 r[jss::result][jss::channels][0u][jss::channel_id] == c ||
1330 r[jss::result][jss::channels][1u][jss::channel_id] == c);
1331 }
1332 }
1333
1334 auto sliceToHex = [](Slice const& slice) {
1335 std::string s;
1336 s.reserve(2 * slice.size());
1337 for (std::uint8_t const byte : slice)
1338 {
1339 s += "0123456789ABCDEF"[((byte & 0xf0) >> 4)];
1340 s += "0123456789ABCDEF"[((byte & 0x0f) >> 0)];
1341 }
1342 return s;
1343 };
1344
1345 {
1346 // Verify chan1 auth
1347 auto const rs = env.rpc("channel_authorize", "alice", chan1Str, "1000");
1348 auto const sig = rs[jss::result][jss::signature].asString();
1349 BEAST_EXPECT(!sig.empty());
1350 {
1351 auto const rv = env.rpc("channel_verify", chan1PkStr, chan1Str, "1000", sig);
1352 BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
1353 }
1354
1355 {
1356 // use pk hex to verify
1357 auto const pkAsHex = sliceToHex(pk.slice());
1358 auto const rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000", sig);
1359 BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
1360 }
1361 {
1362 // malformed amount
1363 auto const pkAsHex = sliceToHex(pk.slice());
1364 auto rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000x", sig);
1365 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1366 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000 ", sig);
1367 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1368 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x1000", sig);
1369 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1370 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x", sig);
1371 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1372 rv = env.rpc("channel_verify", pkAsHex, chan1Str, " ", sig);
1373 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1374 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000 1000", sig);
1375 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1376 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1,000", sig);
1377 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1378 rv = env.rpc("channel_verify", pkAsHex, chan1Str, " 1000", sig);
1379 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1380 rv = env.rpc("channel_verify", pkAsHex, chan1Str, "", sig);
1381 BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
1382 }
1383 {
1384 // malformed channel
1385 auto const pkAsHex = sliceToHex(pk.slice());
1386 auto chan1StrBad = chan1Str;
1387 chan1StrBad.pop_back();
1388 auto rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
1389 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1390 rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
1391 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1392
1393 chan1StrBad = chan1Str;
1394 chan1StrBad.push_back('0');
1395 rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
1396 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1397 rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
1398 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1399
1400 chan1StrBad = chan1Str;
1401 chan1StrBad.back() = 'x';
1402 rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
1403 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1404 rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
1405 BEAST_EXPECT(rv[jss::error] == "channelMalformed");
1406 }
1407 {
1408 // channel_id is not a string.
1410 args[jss::amount] = "2000";
1411 args[jss::channel_id] = 2000;
1412 args[jss::key_type] = "secp256k1";
1413 args[jss::passphrase] = "passphrase_can_be_anything";
1414
1415 auto const ra =
1416 env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1417 BEAST_EXPECT(ra[jss::error] == "invalidParams");
1418 }
1419 {
1420 // channel_id is not a string.
1422 args[jss::public_key] = chan1PkStr;
1423 args[jss::channel_id] = 2000;
1424 args[jss::amount] = "1000";
1425 args[jss::signature] = sig;
1426
1427 auto const rv =
1428 env.rpc("json", "channel_verify", args.toStyledString())[jss::result];
1429 BEAST_EXPECT(rv[jss::error] == "invalidParams");
1430 }
1431 {
1432 // give an ill formed base 58 public key
1433 auto illFormedPk = chan1PkStr.substr(0, chan1PkStr.size() - 1);
1434 auto const rv = env.rpc("channel_verify", illFormedPk, chan1Str, "1000", sig);
1435 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1436 }
1437 {
1438 // give an ill formed hex public key
1439 auto const pkAsHex = sliceToHex(pk.slice());
1440 auto illFormedPk = pkAsHex.substr(0, chan1PkStr.size() - 1);
1441 auto const rv = env.rpc("channel_verify", illFormedPk, chan1Str, "1000", sig);
1442 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1443 }
1444 }
1445 {
1446 // Try to verify chan2 auth with chan1 key
1447 auto const rs = env.rpc("channel_authorize", "alice", chan2Str, "1000");
1448 auto const sig = rs[jss::result][jss::signature].asString();
1449 BEAST_EXPECT(!sig.empty());
1450 {
1451 auto const rv = env.rpc("channel_verify", chan1PkStr, chan1Str, "1000", sig);
1452 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1453 }
1454 {
1455 // use pk hex to verify
1456 auto const pkAsHex = sliceToHex(pk.slice());
1457 auto const rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000", sig);
1458 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1459 }
1460 }
1461 {
1462 // Try to explicitly specify secp256k1 and Ed25519 keys:
1463 auto const chan = to_string(channel(charlie, alice, env.seq(charlie)));
1464 env(create(charlie, alice, channelFunds, settleDelay, charlie.pk()));
1465 env.close();
1466
1467 std::string cpk;
1468 {
1469 auto const r = env.rpc("account_channels", charlie.human(), alice.human());
1470 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1471 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1472 BEAST_EXPECT(r[jss::result][jss::validated]);
1473 cpk = r[jss::result][jss::channels][0u][jss::public_key].asString();
1474 }
1475
1476 // Try to authorize without specifying a key type, expect an error:
1477 auto const rs = env.rpc("channel_authorize", "charlie", chan, "1000");
1478 auto const sig = rs[jss::result][jss::signature].asString();
1479 BEAST_EXPECT(!sig.empty());
1480 {
1481 auto const rv = env.rpc("channel_verify", cpk, chan, "1000", sig);
1482 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1483 }
1484
1485 // Try to authorize using an unknown key type, except an error:
1486 auto const rs1 = env.rpc("channel_authorize", "charlie", "nyx", chan, "1000");
1487 BEAST_EXPECT(rs1[jss::error] == "badKeyType");
1488
1489 // Try to authorize using secp256k1; the authorization _should_
1490 // succeed but the verification should fail:
1491 auto const rs2 = env.rpc("channel_authorize", "charlie", "secp256k1", chan, "1000");
1492 auto const sig2 = rs2[jss::result][jss::signature].asString();
1493 BEAST_EXPECT(!sig2.empty());
1494 {
1495 auto const rv = env.rpc("channel_verify", cpk, chan, "1000", sig2);
1496 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1497 }
1498
1499 // Try to authorize using Ed25519; expect success:
1500 auto const rs3 = env.rpc("channel_authorize", "charlie", "ed25519", chan, "1000");
1501 auto const sig3 = rs3[jss::result][jss::signature].asString();
1502 BEAST_EXPECT(!sig3.empty());
1503 {
1504 auto const rv = env.rpc("channel_verify", cpk, chan, "1000", sig3);
1505 BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
1506 }
1507 }
1508
1509 {
1510 // send malformed amounts rpc requests
1511 auto rs = env.rpc("channel_authorize", "alice", chan1Str, "1000x");
1512 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1513 rs = env.rpc("channel_authorize", "alice", chan1Str, "x1000");
1514 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1515 rs = env.rpc("channel_authorize", "alice", chan1Str, "x");
1516 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1517 {
1518 // Missing channel_id
1520 args[jss::amount] = "2000";
1521 args[jss::key_type] = "secp256k1";
1522 args[jss::passphrase] = "passphrase_can_be_anything";
1523 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1524 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1525 }
1526 {
1527 // Missing amount
1529 args[jss::channel_id] = chan1Str;
1530 args[jss::key_type] = "secp256k1";
1531 args[jss::passphrase] = "passphrase_can_be_anything";
1532 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1533 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1534 }
1535 {
1536 // Missing key_type and no secret.
1538 args[jss::amount] = "2000";
1539 args[jss::channel_id] = chan1Str;
1540 args[jss::passphrase] = "passphrase_can_be_anything";
1541 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1542 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1543 }
1544 {
1545 // Both passphrase and seed specified.
1547 args[jss::amount] = "2000";
1548 args[jss::channel_id] = chan1Str;
1549 args[jss::key_type] = "secp256k1";
1550 args[jss::passphrase] = "passphrase_can_be_anything";
1551 args[jss::seed] = "seed can be anything";
1552 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1553 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1554 }
1555 {
1556 // channel_id is not exact hex.
1558 args[jss::amount] = "2000";
1559 args[jss::channel_id] = chan1Str + "1";
1560 args[jss::key_type] = "secp256k1";
1561 args[jss::passphrase] = "passphrase_can_be_anything";
1562 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1563 BEAST_EXPECT(rs[jss::error] == "channelMalformed");
1564 }
1565 {
1566 // amount is not a string
1568 args[jss::amount] = 2000;
1569 args[jss::channel_id] = chan1Str;
1570 args[jss::key_type] = "secp256k1";
1571 args[jss::passphrase] = "passphrase_can_be_anything";
1572 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1573 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1574 }
1575 {
1576 // Amount is not a decimal string.
1578 args[jss::amount] = "TwoThousand";
1579 args[jss::channel_id] = chan1Str;
1580 args[jss::key_type] = "secp256k1";
1581 args[jss::passphrase] = "passphrase_can_be_anything";
1582 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1583 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1584 }
1585 }
1586 }
1587
1588 void
1590 {
1591 testcase("Optional Fields");
1592 using namespace jtx;
1593 using namespace std::literals::chrono_literals;
1594 Env env{*this, features};
1595 auto const alice = Account("alice");
1596 auto const bob = Account("bob");
1597 auto const carol = Account("carol");
1598 auto const dan = Account("dan");
1599 env.fund(XRP(10000), alice, bob, carol, dan);
1600 auto const pk = alice.pk();
1601 auto const settleDelay = 3600s;
1602 auto const channelFunds = XRP(1000);
1603
1604 std::optional<NetClock::time_point> const cancelAfter;
1605
1606 {
1607 auto const chan = to_string(channel(alice, bob, env.seq(alice)));
1608 env(create(alice, bob, channelFunds, settleDelay, pk));
1609 auto const r = env.rpc("account_channels", alice.human(), bob.human());
1610 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1611 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1612 BEAST_EXPECT(!r[jss::result][jss::channels][0u].isMember(jss::destination_tag));
1613 }
1614 {
1615 std::uint32_t dstTag = 42;
1616 auto const chan = to_string(channel(alice, carol, env.seq(alice)));
1617 env(create(alice, carol, channelFunds, settleDelay, pk, cancelAfter, dstTag));
1618 auto const r = env.rpc("account_channels", alice.human(), carol.human());
1619 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1620 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1621 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::destination_tag] == dstTag);
1622 }
1623 }
1624
1625 void
1627 {
1628 testcase("channel_verify sets kFEE_HEAVY_BURDEN_RPC load type");
1629
1630 using namespace jtx;
1631 using namespace std::literals::chrono_literals;
1632
1633 Env env{*this, features};
1634 auto const alice = Account("alice");
1635 auto const bob = Account("bob");
1636
1637 env.fund(XRP(10000), alice, bob);
1638
1639 auto const pk = alice.pk();
1640 auto const settleDelay = 3600s;
1641 auto const channelFunds = XRP(1000);
1642 auto const chanStr = to_string(channel(alice, bob, env.seq(alice)));
1643
1644 env(create(alice, bob, channelFunds, settleDelay, pk));
1645 env.close();
1646
1647 // Step 1: get a valid signature from channel_authorize
1648 auto const authResult = env.rpc("channel_authorize", "alice", chanStr, "1000");
1649 auto const sig = authResult[jss::result][jss::signature].asString();
1650 BEAST_EXPECT(!sig.empty());
1651 auto const pkHex = strHex(pk.slice());
1652
1653 // Step 2: build rpc::JsonContext directly so we can inspect loadType
1654 auto& app = env.app();
1657 rpc::JsonContext context{
1658 {.j = env.journal,
1659 .app = app,
1660 .loadType = loadType,
1661 .netOps = app.getOPs(),
1662 .ledgerMaster = app.getLedgerMaster(),
1663 .consumer = c,
1664 .role = Role::USER,
1665 .coro = {},
1666 .infoSub = {},
1667 .apiVersion = rpc::kApiVersionIfUnspecified},
1668 {},
1669 {}};
1670 json::Value params;
1671 params[jss::public_key] = pkHex;
1672 params[jss::channel_id] = chanStr;
1673 params[jss::amount] = "1000";
1674 params[jss::signature] = sig;
1675 context.params = std::move(params);
1676
1677 // Confirm default before calling handler
1678 BEAST_EXPECT(context.loadType == resource::kFeeReferenceRpc);
1679 json::Value result;
1680 Gate g;
1681 app.getJobQueue().postCoro(JtClient, "RPC-Client", [&](auto const& coro) {
1682 context.coro = coro;
1683 result = doChannelVerify(context);
1684 g.signal();
1685 });
1686
1687 using namespace std::chrono_literals;
1688 BEAST_EXPECT(g.waitFor(5s));
1689 // Signature must verify correctly
1690 BEAST_EXPECT(result[jss::signature_verified].asBool());
1691 // KEY ASSERTION: loadType must be kFEE_HEAVY_BURDEN_RPC after the fix
1692 // Before fix: this will FAIL because loadType stays kFEE_REFERENCE_RPC (20)
1693 // After fix: this will PASS because loadType is kFEE_HEAVY_BURDEN_RPC (3000)
1694 BEAST_EXPECT(context.loadType == resource::kFeeHeavyBurdenRpc);
1695 // Confirm the charge is 150x heavier than the current (broken) default
1696 BEAST_EXPECT(context.loadType.cost() == resource::kFeeHeavyBurdenRpc.cost()); // 3000
1697 BEAST_EXPECT(context.loadType.cost() != resource::kFeeReferenceRpc.cost()); // not 20
1698 }
1699
1700 void
1702 {
1703 testcase("channel_authorize sets kFEE_HEAVY_BURDEN_RPC load type");
1704
1705 using namespace jtx;
1706 using namespace std::literals::chrono_literals;
1707
1708 Env env{*this, features};
1709 auto const alice = Account("alice");
1710 auto const bob = Account("bob");
1711
1712 env.fund(XRP(10000), alice, bob);
1713
1714 auto const pk = alice.pk();
1715 auto const settleDelay = 3600s;
1716 auto const chanStr = to_string(channel(alice, bob, env.seq(alice)));
1717
1718 env(create(alice, bob, XRP(1000), settleDelay, pk));
1719 env.close();
1720
1721 auto& app = env.app();
1724 rpc::JsonContext context{
1725 {.j = env.journal,
1726 .app = app,
1727 .loadType = loadType,
1728 .netOps = app.getOPs(),
1729 .ledgerMaster = app.getLedgerMaster(),
1730 .consumer = c,
1731 .role = Role::ADMIN, // channel_authorize requires ADMIN or canSign()
1732 .coro = {},
1733 .infoSub = {},
1734 .apiVersion = rpc::kApiVersionIfUnspecified},
1735 {},
1736 {}};
1737 json::Value params;
1738 params[jss::channel_id] = chanStr;
1739 params[jss::amount] = "1000";
1740 params[jss::secret] = alice.name(); // use account name as seed
1741 context.params = std::move(params);
1742
1743 // Confirm default before calling handler
1744 BEAST_EXPECT(context.loadType == resource::kFeeReferenceRpc);
1745 json::Value result;
1746 Gate g;
1747 app.getJobQueue().postCoro(JtClient, "RPC-Client", [&](auto const& coro) {
1748 context.coro = coro;
1749 result = doChannelAuthorize(context);
1750 g.signal();
1751 });
1752
1753 using namespace std::chrono_literals;
1754
1755 BEAST_EXPECT(g.waitFor(5s));
1756 // Must return a valid signature
1757 BEAST_EXPECT(result.isMember(jss::signature));
1758 BEAST_EXPECT(!result[jss::signature].asString().empty());
1759 // KEY ASSERTION: loadType must be kFEE_HEAVY_BURDEN_RPC after the fix
1760 // Before fix: FAILS — stays at kFEE_REFERENCE_RPC (charge=20)
1761 // After fix: PASSES — set to kFEE_HEAVY_BURDEN_RPC (charge=3000)
1762 BEAST_EXPECT(context.loadType == resource::kFeeHeavyBurdenRpc);
1763 }
1764
1765 void
1767 {
1768 testcase("malformed pk");
1769 using namespace jtx;
1770 using namespace std::literals::chrono_literals;
1771 Env env{*this, features};
1772 auto const alice = Account("alice");
1773 auto const bob = Account("bob");
1774 auto usda = alice["USD"];
1775 env.fund(XRP(10000), alice, bob);
1776 auto const pk = alice.pk();
1777 auto const settleDelay = 100s;
1778
1779 auto const chan = channel(alice, bob, env.seq(alice));
1780 auto jv = create(alice, bob, XRP(1000), settleDelay, pk);
1781 auto const pkHex = strHex(pk.slice());
1782 jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2);
1783 env(jv, Ter(temMALFORMED));
1784 jv["PublicKey"] = pkHex.substr(0, pkHex.size() - 2);
1785 env(jv, Ter(temMALFORMED));
1786 auto badPrefix = pkHex;
1787 badPrefix[0] = 'f';
1788 badPrefix[1] = 'f';
1789 jv["PublicKey"] = badPrefix;
1790 env(jv, Ter(temMALFORMED));
1791
1792 jv["PublicKey"] = pkHex;
1793 env(jv);
1794
1795 auto const authAmt = XRP(100);
1796 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
1797 jv = claim(bob, chan, authAmt.value(), authAmt.value(), Slice(sig), alice.pk());
1798 jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2);
1799 env(jv, Ter(temMALFORMED));
1800 jv["PublicKey"] = pkHex.substr(0, pkHex.size() - 2);
1801 env(jv, Ter(temMALFORMED));
1802 badPrefix = pkHex;
1803 badPrefix[0] = 'f';
1804 badPrefix[1] = 'f';
1805 jv["PublicKey"] = badPrefix;
1806 env(jv, Ter(temMALFORMED));
1807
1808 // missing public key
1809 jv.removeMember("PublicKey");
1810 env(jv, Ter(temMALFORMED));
1811
1812 {
1813 auto const txn = R"*(
1814 {
1815
1816 "channel_id":"5DB01B7FFED6B67E6B0414DED11E051D2EE2B7619CE0EAA6286D67A3A4D5BDB3",
1817 "signature":
1818 "304402204EF0AFB78AC23ED1C472E74F4299C0C21F1B21D07EFC0A3838A420F76D783A400220154FB11B6F54320666E4C36CA7F686C16A3A0456800BBC43746F34AF50290064",
1819 "public_key":
1820 "aKijDDiC2q2gXjMpM7i4BUS6cmixgsEe18e7CjsUxwihKfuoFgS5",
1821 "amount": "1000000"
1822 }
1823 )*";
1824 auto const r = env.rpc("json", "channel_verify", txn);
1825 BEAST_EXPECT(r["result"]["error"] == "publicMalformed");
1826 }
1827 }
1828
1829 void
1831 {
1832 testcase("Metadata & Ownership");
1833
1834 using namespace jtx;
1835 using namespace std::literals::chrono_literals;
1836
1837 auto const alice = Account("alice");
1838 auto const bob = Account("bob");
1839 auto const settleDelay = 100s;
1840 auto const pk = alice.pk();
1841
1842 auto inOwnerDir = [](ReadView const& view, Account const& acc, SLE::ConstRef chan) -> bool {
1843 xrpl::Dir const ownerDir(view, keylet::ownerDir(acc.id()));
1844 // NOLINTNEXTLINE(modernize-use-ranges)
1845 return std::find(ownerDir.begin(), ownerDir.end(), chan) != ownerDir.end();
1846 };
1847
1848 auto ownerDirCount = [](ReadView const& view, Account const& acc) -> std::size_t {
1849 xrpl::Dir const ownerDir(view, keylet::ownerDir(acc.id()));
1850 return std::distance(ownerDir.begin(), ownerDir.end());
1851 };
1852
1853 {
1854 // Test with adding the paychan to the recipient's owner directory
1855 Env env{*this, features};
1856 env.fund(XRP(10000), alice, bob);
1857 env(create(alice, bob, XRP(1000), settleDelay, pk));
1858 env.close();
1859 auto const [chan, chanSle] = channelKeyAndSle(*env.current(), alice, bob);
1860 BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
1861 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1);
1862 BEAST_EXPECT(inOwnerDir(*env.current(), bob, chanSle));
1863 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1);
1864 // close the channel
1865 env(claim(bob, chan), Txflags(tfClose));
1866 BEAST_EXPECT(!channelExists(*env.current(), chan));
1867 BEAST_EXPECT(!inOwnerDir(*env.current(), alice, chanSle));
1868 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0);
1869 BEAST_EXPECT(!inOwnerDir(*env.current(), bob, chanSle));
1870 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0);
1871 }
1872
1873 {
1874 // Test removing paychans created before adding to the recipient's
1875 // owner directory
1876 Env env(*this, features);
1877 env.fund(XRP(10000), alice, bob);
1878 // create the channel before the amendment activates
1879 env(create(alice, bob, XRP(1000), settleDelay, pk));
1880 env.close();
1881 auto const [chan, chanSle] = channelKeyAndSle(*env.current(), alice, bob);
1882 BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
1883 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1);
1884 BEAST_EXPECT(inOwnerDir(*env.current(), bob, chanSle));
1885 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1);
1886
1887 env(claim(bob, chan), Txflags(tfClose));
1888 BEAST_EXPECT(!channelExists(*env.current(), chan));
1889 BEAST_EXPECT(!inOwnerDir(*env.current(), alice, chanSle));
1890 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0);
1891 BEAST_EXPECT(!inOwnerDir(*env.current(), bob, chanSle));
1892 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0);
1893 }
1894 }
1895
1896 void
1898 {
1899 testcase("Account Delete");
1900 using namespace test::jtx;
1901 using namespace std::literals::chrono_literals;
1902 auto rmAccount =
1903 [this](
1904 Env& env, Account const& toRm, Account const& dst, TER expectedTer = tesSUCCESS) {
1905 // only allow an account to be deleted if the account's sequence
1906 // number is at least 256 less than the current ledger sequence
1907 for (auto minRmSeq = env.seq(toRm) + 257; env.current()->seq() < minRmSeq;
1908 env.close())
1909 {
1910 }
1911
1912 env(acctdelete(toRm, dst),
1913 Fee(drops(env.current()->fees().increment)),
1914 Ter(expectedTer));
1915 env.close();
1916 this->BEAST_EXPECT(
1917 isTesSuccess(expectedTer) == !env.closed()->exists(keylet::account(toRm.id())));
1918 };
1919
1920 auto const alice = Account("alice");
1921 auto const bob = Account("bob");
1922 auto const carol = Account("carol");
1923
1924 {
1925 Env env{*this, features};
1926 env.fund(XRP(10000), alice, bob, carol);
1927 env.close();
1928
1929 // Create a channel from alice to bob
1930 auto const pk = alice.pk();
1931 auto const settleDelay = 100s;
1932 auto const chan = channel(alice, bob, env.seq(alice));
1933 env(create(alice, bob, XRP(1000), settleDelay, pk));
1934 env.close();
1935 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
1936 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
1937
1938 rmAccount(env, alice, carol, tecHAS_OBLIGATIONS);
1939 rmAccount(env, bob, carol, TER(tecHAS_OBLIGATIONS));
1940
1941 auto const feeDrops = env.current()->fees().base;
1942 auto chanBal = channelBalance(*env.current(), chan);
1943 auto chanAmt = channelAmount(*env.current(), chan);
1944 BEAST_EXPECT(chanBal == XRP(0));
1945 BEAST_EXPECT(chanAmt == XRP(1000));
1946
1947 auto preBob = env.balance(bob);
1948 auto const delta = XRP(50);
1949 auto reqBal = chanBal + delta;
1950 auto authAmt = reqBal + XRP(100);
1951 assert(reqBal <= chanAmt);
1952
1953 env(claim(alice, chan, reqBal, authAmt));
1954 env.close();
1955 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
1956 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
1957 BEAST_EXPECT(env.balance(bob) == preBob + delta);
1958 chanBal = reqBal;
1959
1960 auto const preAlice = env.balance(alice);
1961 env(fund(alice, chan, XRP(1000)));
1962 env.close();
1963 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
1964 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt + XRP(1000));
1965 chanAmt = chanAmt + XRP(1000);
1966
1967 {
1968 // Owner closes, will close after settleDelay
1969 env(claim(alice, chan), Txflags(tfClose));
1970 env.close();
1971 // settle delay hasn't elapsed. Channels should exist.
1972 BEAST_EXPECT(channelExists(*env.current(), chan));
1973 auto const closeTime = env.current()->header().parentCloseTime;
1974 auto const minExpiration = closeTime + settleDelay;
1975 env.close(minExpiration);
1976 env(claim(alice, chan), Txflags(tfClose));
1977 BEAST_EXPECT(!channelExists(*env.current(), chan));
1978 }
1979 }
1980 }
1981
1982 void
1983 testUsingTickets(FeatureBitset features)
1984 {
1985 testcase("using tickets");
1986 using namespace jtx;
1987 using namespace std::literals::chrono_literals;
1988 Env env{*this, features};
1989 auto const alice = Account("alice");
1990 auto const bob = Account("bob");
1991 auto usda = alice["USD"];
1992 env.fund(XRP(10000), alice, bob);
1993
1994 // alice and bob grab enough tickets for all of the following
1995 // transactions. Note that once the tickets are acquired alice's
1996 // and bob's account sequence numbers should not advance.
1997 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
1998 env(ticket::create(alice, 10));
1999 std::uint32_t const aliceSeq{env.seq(alice)};
2000
2001 std::uint32_t bobTicketSeq{env.seq(bob) + 1};
2002 env(ticket::create(bob, 10));
2003 std::uint32_t const bobSeq{env.seq(bob)};
2004
2005 auto const pk = alice.pk();
2006 auto const settleDelay = 100s;
2007 auto const chan = channel(alice, bob, aliceTicketSeq);
2008
2009 env(create(alice, bob, XRP(1000), settleDelay, pk), ticket::Use(aliceTicketSeq++));
2010
2011 env.require(Tickets(alice, env.seq(alice) - aliceTicketSeq));
2012 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2013
2014 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
2015 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
2016
2017 {
2018 auto const preAlice = env.balance(alice);
2019 env(fund(alice, chan, XRP(1000)), ticket::Use(aliceTicketSeq++));
2020
2021 env.require(Tickets(alice, env.seq(alice) - aliceTicketSeq));
2022 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2023
2024 auto const feeDrops = env.current()->fees().base;
2025 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
2026 }
2027
2028 auto chanBal = channelBalance(*env.current(), chan);
2029 auto chanAmt = channelAmount(*env.current(), chan);
2030 BEAST_EXPECT(chanBal == XRP(0));
2031 BEAST_EXPECT(chanAmt == XRP(2000));
2032
2033 {
2034 // No signature needed since the owner is claiming
2035 auto const preBob = env.balance(bob);
2036 auto const delta = XRP(500);
2037 auto const reqBal = chanBal + delta;
2038 auto const authAmt = reqBal + XRP(100);
2039 assert(reqBal <= chanAmt);
2040 env(claim(alice, chan, reqBal, authAmt), ticket::Use(aliceTicketSeq++));
2041
2042 env.require(Tickets(alice, env.seq(alice) - aliceTicketSeq));
2043 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2044
2045 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
2046 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2047 BEAST_EXPECT(env.balance(bob) == preBob + delta);
2048 chanBal = reqBal;
2049 }
2050 {
2051 // Claim with signature
2052 auto preBob = env.balance(bob);
2053 auto const delta = XRP(500);
2054 auto const reqBal = chanBal + delta;
2055 auto const authAmt = reqBal + XRP(100);
2056 assert(reqBal <= chanAmt);
2057 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
2058 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
2059 ticket::Use(bobTicketSeq++));
2060
2061 env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
2062 BEAST_EXPECT(env.seq(bob) == bobSeq);
2063
2064 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
2065 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2066 auto const feeDrops = env.current()->fees().base;
2067 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
2068 chanBal = reqBal;
2069
2070 // claim again
2071 preBob = env.balance(bob);
2072 // A transaction that generates a tec still consumes its ticket.
2073 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
2074 ticket::Use(bobTicketSeq++),
2075 Ter(tecUNFUNDED_PAYMENT));
2076
2077 env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
2078 BEAST_EXPECT(env.seq(bob) == bobSeq);
2079
2080 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2081 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2082 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
2083 }
2084 {
2085 // Try to claim more than authorized
2086 auto const preBob = env.balance(bob);
2087 STAmount const authAmt = chanBal + XRP(500);
2088 STAmount const reqAmt = authAmt + drops(1);
2089 assert(reqAmt <= chanAmt);
2090 // Note that since claim() returns a tem (neither tec nor tes),
2091 // the ticket is not consumed. So we don't kIncrement bobTicket.
2092 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
2093 env(claim(bob, chan, reqAmt, authAmt, Slice(sig), alice.pk()),
2094 ticket::Use(bobTicketSeq),
2095 Ter(temBAD_AMOUNT));
2096
2097 env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
2098 BEAST_EXPECT(env.seq(bob) == bobSeq);
2099
2100 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2101 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2102 BEAST_EXPECT(env.balance(bob) == preBob);
2103 }
2104
2105 // Dst tries to fund the channel
2106 env(fund(bob, chan, XRP(1000)), ticket::Use(bobTicketSeq++), Ter(tecNO_PERMISSION));
2107
2108 env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
2109 BEAST_EXPECT(env.seq(bob) == bobSeq);
2110
2111 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2112 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2113
2114 {
2115 // Dst closes channel
2116 auto const preAlice = env.balance(alice);
2117 auto const preBob = env.balance(bob);
2118 env(claim(bob, chan), Txflags(tfClose), ticket::Use(bobTicketSeq++));
2119
2120 env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
2121 BEAST_EXPECT(env.seq(bob) == bobSeq);
2122
2123 BEAST_EXPECT(!channelExists(*env.current(), chan));
2124 auto const feeDrops = env.current()->fees().base;
2125 auto const delta = chanAmt - chanBal;
2126 assert(delta > beast::kZero);
2127 BEAST_EXPECT(env.balance(alice) == preAlice + delta);
2128 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
2129 }
2130 env.require(Tickets(alice, env.seq(alice) - aliceTicketSeq));
2131 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2132 env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
2133 BEAST_EXPECT(env.seq(bob) == bobSeq);
2134 }
2135
2136 void
2138 {
2139 testSimple(features);
2140 testDisallowIncoming(features);
2141 testCancelAfter(features);
2142 testSettleDelay(features);
2143 testExpiration(features);
2144 testCloseDry(features);
2145 testDefaultAmount(features);
2146 testDisallowXRP(features);
2147 testDstTag(features);
2148 testDepositAuth(features);
2149 testMultiple(features);
2150 testAccountChannelsRPC(features);
2154 testAuthVerifyRPC(features);
2155 testOptionalFields(features);
2157 testMetaAndOwnership(features);
2158 testAccountDelete(features);
2159 testUsingTickets(features);
2160 testChannelVerifyLoadType(features);
2162 }
2163
2164public:
2165 void
2166 run() override
2167 {
2168 using namespace test::jtx;
2169 // fixCleanup3_2_0 changes payment-channel error codes (tem* -> tec*)
2170 // and channel-closing semantics. This suite asserts the
2171 // pre-amendment behavior, so run it with the amendment disabled.
2172 FeatureBitset const all{testableAmendments() - fixCleanup3_2_0};
2173 testWithFeats(all);
2175 testMetaAndOwnership(all - fixIncludeKeyletFields);
2176 }
2177};
2178
2180} // namespace xrpl::test
A testsuite class.
Definition suite.h:52
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
std::string toStyledString() const
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
Like std::vector<char> but better.
Definition Buffer.h:19
A class that simplifies iterating ledger directory pages.
Definition Dir.h:30
std::chrono::time_point< NetClock > time_point
Definition chrono.h:48
A public key.
Definition PublicKey.h:53
A view into a ledger.
Definition ReadView.h:41
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
XRPAmount xrp() const
Definition STAmount.cpp:272
std::shared_ptr< STLedgerEntry const > const & ConstRef
A secret key.
Definition SecretKey.h:24
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:62
Slice slice() const noexcept
Definition Serializer.h:141
An immutable linear range of bytes.
Definition Slice.h:28
A consumption charge.
Definition Charge.h:13
value_type cost() const
Return the cost of the charge in resource::Manager units.
Definition Charge.cpp:22
An endpoint that consumes resources.
Definition Consumer.h:20
Immutable cryptographic account descriptor.
Definition jtx/Account.h:21
PublicKey const & pk() const
Return the public key.
Definition jtx/Account.h:84
A transaction testing environment.
Definition Env.h:161
Application & app()
Definition Env.h:300
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:323
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:302
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:1058
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:201
beast::Journal const journal
Definition Env.h:204
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:377
bool waitFor(std::chrono::duration< Rep, Period > const &relTime)
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:18
Set the flags on a JTx.
Definition txflags.h:14
T distance(T... args)
T emplace_back(T... args)
T empty(T... args)
T find(T... args)
T insert(T... args)
constexpr Zero kZero
Definition Zero.h:30
@ Array
array value (ordered list)
Definition json_value.h:28
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
@ Null
'null' value
Definition json_value.h:22
Keylet payChannel(AccountID const &src, AccountID const &dst, SeqProxy const &seq) noexcept
A PaymentChannel.
Definition Indexes.cpp:424
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:403
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
Charge const kFeeHeavyBurdenRpc
Charge const kFeeReferenceRpc
static constexpr auto kApiVersionIfUnspecified
Definition ApiVersion.h:44
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:16
json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:56
json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:38
json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:16
json::Value unauth(Account const &account, Account const &unauth)
Remove pre-authorization for deposit.
Definition deposit.cpp:27
json::Value claim(AccountID const &account, UInt256 const &channel, std::optional< STAmount > const &balance, std::optional< STAmount > const &amount, std::optional< Slice > const &signature, std::optional< PublicKey > const &pk)
json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount, NetClock::duration const &settleDelay, PublicKey const &pk, std::optional< NetClock::time_point > const &cancelAfter, std::optional< std::uint32_t > const &dstTag)
STAmount channelBalance(ReadView const &view, UInt256 const &chan)
bool channelExists(ReadView const &view, UInt256 const &chan)
UInt256 channel(AccountID const &account, AccountID const &dst, std::uint32_t seqProxyValue)
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:16
void sign(json::Value &jv, Account const &account, json::Value &sigObject, HashPrefix prefix)
Sign automatically into a specific Json field of the jv object.
Definition utility.cpp:52
std::vector< STAmount > fund(jtx::Env &env, jtx::Account const &gw, std::vector< jtx::Account > const &accounts, std::vector< STAmount > const &amts, Fund how)
Definition AMMTest.cpp:34
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:110
FeatureBitset testableAmendments()
Definition Env.h:92
json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
OwnerCount< ltTICKET > Tickets
Match the number of tickets on the account.
Definition ticket.h:54
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:15
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:54
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void serializePayChanAuthorization(Serializer &msg, UInt256 const &key, XRPAmount const &amt)
Definition PayChan.h:11
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition ApiVersion.h:159
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
@ USER
Definition Role.h:27
@ ADMIN
Definition Role.h:27
BaseUInt< 256 > UInt256
Definition base_uint.h:580
@ JtClient
Definition Job.h:31
json::Value doChannelVerify(rpc::JsonContext &context)
static std::string sliceToHex(Slice const &slice)
Definition PublicKey.cpp:77
@ temBAD_EXPIRATION
Definition TER.h:79
@ temDST_IS_SRC
Definition TER.h:96
@ temMALFORMED
Definition TER.h:75
@ temDISABLED
Definition TER.h:102
@ temBAD_AMOUNT
Definition TER.h:77
@ temBAD_SIGNATURE
Definition TER.h:93
@ temBAD_SIGNER
Definition TER.h:103
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecUNFUNDED_PAYMENT
Definition TER.h:293
@ tecNO_ENTRY
Definition TER.h:314
@ tecBAD_CREDENTIALS
Definition TER.h:367
@ tecEXPIRED
Definition TER.h:322
@ tecNO_PERMISSION
Definition TER.h:313
@ tecDST_TAG_NEEDED
Definition TER.h:317
@ tecHAS_OBLIGATIONS
Definition TER.h:325
@ tecNO_DST
Definition TER.h:298
@ tecUNFUNDED
Definition TER.h:303
json::Value doChannelAuthorize(rpc::JsonContext &context)
@ tesSUCCESS
Definition TER.h:250
T reserve(T... args)
T size(T... args)
std::shared_ptr< JobQueue::Coro > coro
Definition Context.h:35
resource::Charge & loadType
Definition Context.h:30
json::Value params
Definition Context.h:51
void testSettleDelay(FeatureBitset features)
void testOptionalFields(FeatureBitset features)
void testSimple(FeatureBitset features)
void testMetaAndOwnership(FeatureBitset features)
void testMultiple(FeatureBitset features)
void testCancelAfter(FeatureBitset features)
void testWithFeats(FeatureBitset features)
static Buffer signClaimAuth(PublicKey const &pk, SecretKey const &sk, UInt256 const &channel, STAmount const &authAmt)
void testDepositAuth(FeatureBitset features)
void testDefaultAmount(FeatureBitset features)
void testCloseDry(FeatureBitset features)
void testUsingTickets(FeatureBitset features)
void testDstTag(FeatureBitset features)
void testChannelAuthorizeLoadType(FeatureBitset features)
void testAccountChannelAuthorize(FeatureBitset features)
static STAmount channelAmount(ReadView const &view, UInt256 const &chan)
void testAccountChannelsRPC(FeatureBitset features)
void testExpiration(FeatureBitset features)
static std::optional< std::int64_t > channelExpiration(ReadView const &view, UInt256 const &chan)
void testDisallowIncoming(FeatureBitset features)
void testChannelVerifyLoadType(FeatureBitset features)
void testAuthVerifyRPC(FeatureBitset features)
void testAccountDelete(FeatureBitset features)
void run() override
Runs the suite.
static std::pair< UInt256, SLE::const_pointer > channelKeyAndSle(ReadView const &view, jtx::Account const &account, jtx::Account const &dst)
void testDisallowXRP(FeatureBitset features)
void testAccountChannelsRPCSenderOnly(FeatureBitset features)
void testMalformedPK(FeatureBitset features)
void testAccountChannelsRPCMarkers(FeatureBitset features)
T substr(T... args)
T to_string(T... args)