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 // give an ill formed base 58 public key
1409 auto illFormedPk = chan1PkStr.substr(0, chan1PkStr.size() - 1);
1410 auto const rv = env.rpc("channel_verify", illFormedPk, chan1Str, "1000", sig);
1411 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1412 }
1413 {
1414 // give an ill formed hex public key
1415 auto const pkAsHex = sliceToHex(pk.slice());
1416 auto illFormedPk = pkAsHex.substr(0, chan1PkStr.size() - 1);
1417 auto const rv = env.rpc("channel_verify", illFormedPk, chan1Str, "1000", sig);
1418 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1419 }
1420 }
1421 {
1422 // Try to verify chan2 auth with chan1 key
1423 auto const rs = env.rpc("channel_authorize", "alice", chan2Str, "1000");
1424 auto const sig = rs[jss::result][jss::signature].asString();
1425 BEAST_EXPECT(!sig.empty());
1426 {
1427 auto const rv = env.rpc("channel_verify", chan1PkStr, chan1Str, "1000", sig);
1428 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1429 }
1430 {
1431 // use pk hex to verify
1432 auto const pkAsHex = sliceToHex(pk.slice());
1433 auto const rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000", sig);
1434 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1435 }
1436 }
1437 {
1438 // Try to explicitly specify secp256k1 and Ed25519 keys:
1439 auto const chan = to_string(channel(charlie, alice, env.seq(charlie)));
1440 env(create(charlie, alice, channelFunds, settleDelay, charlie.pk()));
1441 env.close();
1442
1443 std::string cpk;
1444 {
1445 auto const r = env.rpc("account_channels", charlie.human(), alice.human());
1446 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1447 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1448 BEAST_EXPECT(r[jss::result][jss::validated]);
1449 cpk = r[jss::result][jss::channels][0u][jss::public_key].asString();
1450 }
1451
1452 // Try to authorize without specifying a key type, expect an error:
1453 auto const rs = env.rpc("channel_authorize", "charlie", chan, "1000");
1454 auto const sig = rs[jss::result][jss::signature].asString();
1455 BEAST_EXPECT(!sig.empty());
1456 {
1457 auto const rv = env.rpc("channel_verify", cpk, chan, "1000", sig);
1458 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1459 }
1460
1461 // Try to authorize using an unknown key type, except an error:
1462 auto const rs1 = env.rpc("channel_authorize", "charlie", "nyx", chan, "1000");
1463 BEAST_EXPECT(rs1[jss::error] == "badKeyType");
1464
1465 // Try to authorize using secp256k1; the authorization _should_
1466 // succeed but the verification should fail:
1467 auto const rs2 = env.rpc("channel_authorize", "charlie", "secp256k1", chan, "1000");
1468 auto const sig2 = rs2[jss::result][jss::signature].asString();
1469 BEAST_EXPECT(!sig2.empty());
1470 {
1471 auto const rv = env.rpc("channel_verify", cpk, chan, "1000", sig2);
1472 BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
1473 }
1474
1475 // Try to authorize using Ed25519; expect success:
1476 auto const rs3 = env.rpc("channel_authorize", "charlie", "ed25519", chan, "1000");
1477 auto const sig3 = rs3[jss::result][jss::signature].asString();
1478 BEAST_EXPECT(!sig3.empty());
1479 {
1480 auto const rv = env.rpc("channel_verify", cpk, chan, "1000", sig3);
1481 BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
1482 }
1483 }
1484
1485 {
1486 // send malformed amounts rpc requests
1487 auto rs = env.rpc("channel_authorize", "alice", chan1Str, "1000x");
1488 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1489 rs = env.rpc("channel_authorize", "alice", chan1Str, "x1000");
1490 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1491 rs = env.rpc("channel_authorize", "alice", chan1Str, "x");
1492 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1493 {
1494 // Missing channel_id
1496 args[jss::amount] = "2000";
1497 args[jss::key_type] = "secp256k1";
1498 args[jss::passphrase] = "passphrase_can_be_anything";
1499 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1500 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1501 }
1502 {
1503 // Missing amount
1505 args[jss::channel_id] = chan1Str;
1506 args[jss::key_type] = "secp256k1";
1507 args[jss::passphrase] = "passphrase_can_be_anything";
1508 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1509 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1510 }
1511 {
1512 // Missing key_type and no secret.
1514 args[jss::amount] = "2000";
1515 args[jss::channel_id] = chan1Str;
1516 args[jss::passphrase] = "passphrase_can_be_anything";
1517 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1518 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1519 }
1520 {
1521 // Both passphrase and seed specified.
1523 args[jss::amount] = "2000";
1524 args[jss::channel_id] = chan1Str;
1525 args[jss::key_type] = "secp256k1";
1526 args[jss::passphrase] = "passphrase_can_be_anything";
1527 args[jss::seed] = "seed can be anything";
1528 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1529 BEAST_EXPECT(rs[jss::error] == "invalidParams");
1530 }
1531 {
1532 // channel_id is not exact hex.
1534 args[jss::amount] = "2000";
1535 args[jss::channel_id] = chan1Str + "1";
1536 args[jss::key_type] = "secp256k1";
1537 args[jss::passphrase] = "passphrase_can_be_anything";
1538 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1539 BEAST_EXPECT(rs[jss::error] == "channelMalformed");
1540 }
1541 {
1542 // amount is not a string
1544 args[jss::amount] = 2000;
1545 args[jss::channel_id] = chan1Str;
1546 args[jss::key_type] = "secp256k1";
1547 args[jss::passphrase] = "passphrase_can_be_anything";
1548 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1549 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1550 }
1551 {
1552 // Amount is not a decimal string.
1554 args[jss::amount] = "TwoThousand";
1555 args[jss::channel_id] = chan1Str;
1556 args[jss::key_type] = "secp256k1";
1557 args[jss::passphrase] = "passphrase_can_be_anything";
1558 rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
1559 BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
1560 }
1561 }
1562 }
1563
1564 void
1566 {
1567 testcase("Optional Fields");
1568 using namespace jtx;
1569 using namespace std::literals::chrono_literals;
1570 Env env{*this, features};
1571 auto const alice = Account("alice");
1572 auto const bob = Account("bob");
1573 auto const carol = Account("carol");
1574 auto const dan = Account("dan");
1575 env.fund(XRP(10000), alice, bob, carol, dan);
1576 auto const pk = alice.pk();
1577 auto const settleDelay = 3600s;
1578 auto const channelFunds = XRP(1000);
1579
1580 std::optional<NetClock::time_point> const cancelAfter;
1581
1582 {
1583 auto const chan = to_string(channel(alice, bob, env.seq(alice)));
1584 env(create(alice, bob, channelFunds, settleDelay, pk));
1585 auto const r = env.rpc("account_channels", alice.human(), bob.human());
1586 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1587 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1588 BEAST_EXPECT(!r[jss::result][jss::channels][0u].isMember(jss::destination_tag));
1589 }
1590 {
1591 std::uint32_t dstTag = 42;
1592 auto const chan = to_string(channel(alice, carol, env.seq(alice)));
1593 env(create(alice, carol, channelFunds, settleDelay, pk, cancelAfter, dstTag));
1594 auto const r = env.rpc("account_channels", alice.human(), carol.human());
1595 BEAST_EXPECT(r[jss::result][jss::channels].size() == 1);
1596 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::channel_id] == chan);
1597 BEAST_EXPECT(r[jss::result][jss::channels][0u][jss::destination_tag] == dstTag);
1598 }
1599 }
1600
1601 void
1603 {
1604 testcase("channel_verify sets kFEE_HEAVY_BURDEN_RPC load type");
1605
1606 using namespace jtx;
1607 using namespace std::literals::chrono_literals;
1608
1609 Env env{*this, features};
1610 auto const alice = Account("alice");
1611 auto const bob = Account("bob");
1612
1613 env.fund(XRP(10000), alice, bob);
1614
1615 auto const pk = alice.pk();
1616 auto const settleDelay = 3600s;
1617 auto const channelFunds = XRP(1000);
1618 auto const chanStr = to_string(channel(alice, bob, env.seq(alice)));
1619
1620 env(create(alice, bob, channelFunds, settleDelay, pk));
1621 env.close();
1622
1623 // Step 1: get a valid signature from channel_authorize
1624 auto const authResult = env.rpc("channel_authorize", "alice", chanStr, "1000");
1625 auto const sig = authResult[jss::result][jss::signature].asString();
1626 BEAST_EXPECT(!sig.empty());
1627 auto const pkHex = strHex(pk.slice());
1628
1629 // Step 2: build rpc::JsonContext directly so we can inspect loadType
1630 auto& app = env.app();
1633 rpc::JsonContext context{
1634 {.j = env.journal,
1635 .app = app,
1636 .loadType = loadType,
1637 .netOps = app.getOPs(),
1638 .ledgerMaster = app.getLedgerMaster(),
1639 .consumer = c,
1640 .role = Role::USER,
1641 .coro = {},
1642 .infoSub = {},
1643 .apiVersion = rpc::kApiVersionIfUnspecified},
1644 {},
1645 {}};
1646 json::Value params;
1647 params[jss::public_key] = pkHex;
1648 params[jss::channel_id] = chanStr;
1649 params[jss::amount] = "1000";
1650 params[jss::signature] = sig;
1651 context.params = std::move(params);
1652
1653 // Confirm default before calling handler
1654 BEAST_EXPECT(context.loadType == resource::kFeeReferenceRpc);
1655 json::Value result;
1656 Gate g;
1657 app.getJobQueue().postCoro(JtClient, "RPC-Client", [&](auto const& coro) {
1658 context.coro = coro;
1659 result = doChannelVerify(context);
1660 g.signal();
1661 });
1662
1663 using namespace std::chrono_literals;
1664 BEAST_EXPECT(g.waitFor(5s));
1665 // Signature must verify correctly
1666 BEAST_EXPECT(result[jss::signature_verified].asBool());
1667 // KEY ASSERTION: loadType must be kFEE_HEAVY_BURDEN_RPC after the fix
1668 // Before fix: this will FAIL because loadType stays kFEE_REFERENCE_RPC (20)
1669 // After fix: this will PASS because loadType is kFEE_HEAVY_BURDEN_RPC (3000)
1670 BEAST_EXPECT(context.loadType == resource::kFeeHeavyBurdenRpc);
1671 // Confirm the charge is 150x heavier than the current (broken) default
1672 BEAST_EXPECT(context.loadType.cost() == resource::kFeeHeavyBurdenRpc.cost()); // 3000
1673 BEAST_EXPECT(context.loadType.cost() != resource::kFeeReferenceRpc.cost()); // not 20
1674 }
1675
1676 void
1678 {
1679 testcase("channel_authorize sets kFEE_HEAVY_BURDEN_RPC load type");
1680
1681 using namespace jtx;
1682 using namespace std::literals::chrono_literals;
1683
1684 Env env{*this, features};
1685 auto const alice = Account("alice");
1686 auto const bob = Account("bob");
1687
1688 env.fund(XRP(10000), alice, bob);
1689
1690 auto const pk = alice.pk();
1691 auto const settleDelay = 3600s;
1692 auto const chanStr = to_string(channel(alice, bob, env.seq(alice)));
1693
1694 env(create(alice, bob, XRP(1000), settleDelay, pk));
1695 env.close();
1696
1697 auto& app = env.app();
1700 rpc::JsonContext context{
1701 {.j = env.journal,
1702 .app = app,
1703 .loadType = loadType,
1704 .netOps = app.getOPs(),
1705 .ledgerMaster = app.getLedgerMaster(),
1706 .consumer = c,
1707 .role = Role::ADMIN, // channel_authorize requires ADMIN or canSign()
1708 .coro = {},
1709 .infoSub = {},
1710 .apiVersion = rpc::kApiVersionIfUnspecified},
1711 {},
1712 {}};
1713 json::Value params;
1714 params[jss::channel_id] = chanStr;
1715 params[jss::amount] = "1000";
1716 params[jss::secret] = alice.name(); // use account name as seed
1717 context.params = std::move(params);
1718
1719 // Confirm default before calling handler
1720 BEAST_EXPECT(context.loadType == resource::kFeeReferenceRpc);
1721 json::Value result;
1722 Gate g;
1723 app.getJobQueue().postCoro(JtClient, "RPC-Client", [&](auto const& coro) {
1724 context.coro = coro;
1725 result = doChannelAuthorize(context);
1726 g.signal();
1727 });
1728
1729 using namespace std::chrono_literals;
1730
1731 BEAST_EXPECT(g.waitFor(5s));
1732 // Must return a valid signature
1733 BEAST_EXPECT(result.isMember(jss::signature));
1734 BEAST_EXPECT(!result[jss::signature].asString().empty());
1735 // KEY ASSERTION: loadType must be kFEE_HEAVY_BURDEN_RPC after the fix
1736 // Before fix: FAILS — stays at kFEE_REFERENCE_RPC (charge=20)
1737 // After fix: PASSES — set to kFEE_HEAVY_BURDEN_RPC (charge=3000)
1738 BEAST_EXPECT(context.loadType == resource::kFeeHeavyBurdenRpc);
1739 }
1740
1741 void
1743 {
1744 testcase("malformed pk");
1745 using namespace jtx;
1746 using namespace std::literals::chrono_literals;
1747 Env env{*this, features};
1748 auto const alice = Account("alice");
1749 auto const bob = Account("bob");
1750 auto usda = alice["USD"];
1751 env.fund(XRP(10000), alice, bob);
1752 auto const pk = alice.pk();
1753 auto const settleDelay = 100s;
1754
1755 auto const chan = channel(alice, bob, env.seq(alice));
1756 auto jv = create(alice, bob, XRP(1000), settleDelay, pk);
1757 auto const pkHex = strHex(pk.slice());
1758 jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2);
1759 env(jv, Ter(temMALFORMED));
1760 jv["PublicKey"] = pkHex.substr(0, pkHex.size() - 2);
1761 env(jv, Ter(temMALFORMED));
1762 auto badPrefix = pkHex;
1763 badPrefix[0] = 'f';
1764 badPrefix[1] = 'f';
1765 jv["PublicKey"] = badPrefix;
1766 env(jv, Ter(temMALFORMED));
1767
1768 jv["PublicKey"] = pkHex;
1769 env(jv);
1770
1771 auto const authAmt = XRP(100);
1772 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
1773 jv = claim(bob, chan, authAmt.value(), authAmt.value(), Slice(sig), alice.pk());
1774 jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2);
1775 env(jv, Ter(temMALFORMED));
1776 jv["PublicKey"] = pkHex.substr(0, pkHex.size() - 2);
1777 env(jv, Ter(temMALFORMED));
1778 badPrefix = pkHex;
1779 badPrefix[0] = 'f';
1780 badPrefix[1] = 'f';
1781 jv["PublicKey"] = badPrefix;
1782 env(jv, Ter(temMALFORMED));
1783
1784 // missing public key
1785 jv.removeMember("PublicKey");
1786 env(jv, Ter(temMALFORMED));
1787
1788 {
1789 auto const txn = R"*(
1790 {
1791
1792 "channel_id":"5DB01B7FFED6B67E6B0414DED11E051D2EE2B7619CE0EAA6286D67A3A4D5BDB3",
1793 "signature":
1794 "304402204EF0AFB78AC23ED1C472E74F4299C0C21F1B21D07EFC0A3838A420F76D783A400220154FB11B6F54320666E4C36CA7F686C16A3A0456800BBC43746F34AF50290064",
1795 "public_key":
1796 "aKijDDiC2q2gXjMpM7i4BUS6cmixgsEe18e7CjsUxwihKfuoFgS5",
1797 "amount": "1000000"
1798 }
1799 )*";
1800 auto const r = env.rpc("json", "channel_verify", txn);
1801 BEAST_EXPECT(r["result"]["error"] == "publicMalformed");
1802 }
1803 }
1804
1805 void
1807 {
1808 testcase("Metadata & Ownership");
1809
1810 using namespace jtx;
1811 using namespace std::literals::chrono_literals;
1812
1813 auto const alice = Account("alice");
1814 auto const bob = Account("bob");
1815 auto const settleDelay = 100s;
1816 auto const pk = alice.pk();
1817
1818 auto inOwnerDir =
1819 [](ReadView const& view, Account const& acc, SLE::const_ref chan) -> bool {
1820 xrpl::Dir const ownerDir(view, keylet::ownerDir(acc.id()));
1821 // NOLINTNEXTLINE(modernize-use-ranges)
1822 return std::find(ownerDir.begin(), ownerDir.end(), chan) != ownerDir.end();
1823 };
1824
1825 auto ownerDirCount = [](ReadView const& view, Account const& acc) -> std::size_t {
1826 xrpl::Dir const ownerDir(view, keylet::ownerDir(acc.id()));
1827 return std::distance(ownerDir.begin(), ownerDir.end());
1828 };
1829
1830 {
1831 // Test with adding the paychan to the recipient's owner directory
1832 Env env{*this, features};
1833 env.fund(XRP(10000), alice, bob);
1834 env(create(alice, bob, XRP(1000), settleDelay, pk));
1835 env.close();
1836 auto const [chan, chanSle] = channelKeyAndSle(*env.current(), alice, bob);
1837 BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
1838 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1);
1839 BEAST_EXPECT(inOwnerDir(*env.current(), bob, chanSle));
1840 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1);
1841 // close the channel
1842 env(claim(bob, chan), Txflags(tfClose));
1843 BEAST_EXPECT(!channelExists(*env.current(), chan));
1844 BEAST_EXPECT(!inOwnerDir(*env.current(), alice, chanSle));
1845 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0);
1846 BEAST_EXPECT(!inOwnerDir(*env.current(), bob, chanSle));
1847 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0);
1848 }
1849
1850 {
1851 // Test removing paychans created before adding to the recipient's
1852 // owner directory
1853 Env env(*this, features);
1854 env.fund(XRP(10000), alice, bob);
1855 // create the channel before the amendment activates
1856 env(create(alice, bob, XRP(1000), settleDelay, pk));
1857 env.close();
1858 auto const [chan, chanSle] = channelKeyAndSle(*env.current(), alice, bob);
1859 BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
1860 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1);
1861 BEAST_EXPECT(inOwnerDir(*env.current(), bob, chanSle));
1862 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1);
1863
1864 env(claim(bob, chan), Txflags(tfClose));
1865 BEAST_EXPECT(!channelExists(*env.current(), chan));
1866 BEAST_EXPECT(!inOwnerDir(*env.current(), alice, chanSle));
1867 BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0);
1868 BEAST_EXPECT(!inOwnerDir(*env.current(), bob, chanSle));
1869 BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0);
1870 }
1871 }
1872
1873 void
1875 {
1876 testcase("Account Delete");
1877 using namespace test::jtx;
1878 using namespace std::literals::chrono_literals;
1879 auto rmAccount =
1880 [this](
1881 Env& env, Account const& toRm, Account const& dst, TER expectedTer = tesSUCCESS) {
1882 // only allow an account to be deleted if the account's sequence
1883 // number is at least 256 less than the current ledger sequence
1884 for (auto minRmSeq = env.seq(toRm) + 257; env.current()->seq() < minRmSeq;
1885 env.close())
1886 {
1887 }
1888
1889 env(acctdelete(toRm, dst),
1890 Fee(drops(env.current()->fees().increment)),
1891 Ter(expectedTer));
1892 env.close();
1893 this->BEAST_EXPECT(
1894 isTesSuccess(expectedTer) == !env.closed()->exists(keylet::account(toRm.id())));
1895 };
1896
1897 auto const alice = Account("alice");
1898 auto const bob = Account("bob");
1899 auto const carol = Account("carol");
1900
1901 {
1902 Env env{*this, features};
1903 env.fund(XRP(10000), alice, bob, carol);
1904 env.close();
1905
1906 // Create a channel from alice to bob
1907 auto const pk = alice.pk();
1908 auto const settleDelay = 100s;
1909 auto const chan = channel(alice, bob, env.seq(alice));
1910 env(create(alice, bob, XRP(1000), settleDelay, pk));
1911 env.close();
1912 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
1913 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
1914
1915 rmAccount(env, alice, carol, tecHAS_OBLIGATIONS);
1916 rmAccount(env, bob, carol, TER(tecHAS_OBLIGATIONS));
1917
1918 auto const feeDrops = env.current()->fees().base;
1919 auto chanBal = channelBalance(*env.current(), chan);
1920 auto chanAmt = channelAmount(*env.current(), chan);
1921 BEAST_EXPECT(chanBal == XRP(0));
1922 BEAST_EXPECT(chanAmt == XRP(1000));
1923
1924 auto preBob = env.balance(bob);
1925 auto const delta = XRP(50);
1926 auto reqBal = chanBal + delta;
1927 auto authAmt = reqBal + XRP(100);
1928 assert(reqBal <= chanAmt);
1929
1930 env(claim(alice, chan, reqBal, authAmt));
1931 env.close();
1932 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
1933 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
1934 BEAST_EXPECT(env.balance(bob) == preBob + delta);
1935 chanBal = reqBal;
1936
1937 auto const preAlice = env.balance(alice);
1938 env(fund(alice, chan, XRP(1000)));
1939 env.close();
1940 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
1941 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt + XRP(1000));
1942 chanAmt = chanAmt + XRP(1000);
1943
1944 {
1945 // Owner closes, will close after settleDelay
1946 env(claim(alice, chan), Txflags(tfClose));
1947 env.close();
1948 // settle delay hasn't elapsed. Channels should exist.
1949 BEAST_EXPECT(channelExists(*env.current(), chan));
1950 auto const closeTime = env.current()->header().parentCloseTime;
1951 auto const minExpiration = closeTime + settleDelay;
1952 env.close(minExpiration);
1953 env(claim(alice, chan), Txflags(tfClose));
1954 BEAST_EXPECT(!channelExists(*env.current(), chan));
1955 }
1956 }
1957 }
1958
1959 void
1960 testUsingTickets(FeatureBitset features)
1961 {
1962 testcase("using tickets");
1963 using namespace jtx;
1964 using namespace std::literals::chrono_literals;
1965 Env env{*this, features};
1966 auto const alice = Account("alice");
1967 auto const bob = Account("bob");
1968 auto usda = alice["USD"];
1969 env.fund(XRP(10000), alice, bob);
1970
1971 // alice and bob grab enough tickets for all of the following
1972 // transactions. Note that once the tickets are acquired alice's
1973 // and bob's account sequence numbers should not advance.
1974 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
1975 env(ticket::create(alice, 10));
1976 std::uint32_t const aliceSeq{env.seq(alice)};
1977
1978 std::uint32_t bobTicketSeq{env.seq(bob) + 1};
1979 env(ticket::create(bob, 10));
1980 std::uint32_t const bobSeq{env.seq(bob)};
1981
1982 auto const pk = alice.pk();
1983 auto const settleDelay = 100s;
1984 auto const chan = channel(alice, bob, aliceTicketSeq);
1985
1986 env(create(alice, bob, XRP(1000), settleDelay, pk), ticket::Use(aliceTicketSeq++));
1987
1988 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1989 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1990
1991 BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
1992 BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
1993
1994 {
1995 auto const preAlice = env.balance(alice);
1996 env(fund(alice, chan, XRP(1000)), ticket::Use(aliceTicketSeq++));
1997
1998 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1999 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2000
2001 auto const feeDrops = env.current()->fees().base;
2002 BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
2003 }
2004
2005 auto chanBal = channelBalance(*env.current(), chan);
2006 auto chanAmt = channelAmount(*env.current(), chan);
2007 BEAST_EXPECT(chanBal == XRP(0));
2008 BEAST_EXPECT(chanAmt == XRP(2000));
2009
2010 {
2011 // No signature needed since the owner is claiming
2012 auto const preBob = env.balance(bob);
2013 auto const delta = XRP(500);
2014 auto const reqBal = chanBal + delta;
2015 auto const authAmt = reqBal + XRP(100);
2016 assert(reqBal <= chanAmt);
2017 env(claim(alice, chan, reqBal, authAmt), ticket::Use(aliceTicketSeq++));
2018
2019 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
2020 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2021
2022 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
2023 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2024 BEAST_EXPECT(env.balance(bob) == preBob + delta);
2025 chanBal = reqBal;
2026 }
2027 {
2028 // Claim with signature
2029 auto preBob = env.balance(bob);
2030 auto const delta = XRP(500);
2031 auto const reqBal = chanBal + delta;
2032 auto const authAmt = reqBal + XRP(100);
2033 assert(reqBal <= chanAmt);
2034 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
2035 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
2036 ticket::Use(bobTicketSeq++));
2037
2038 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2039 BEAST_EXPECT(env.seq(bob) == bobSeq);
2040
2041 BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
2042 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2043 auto const feeDrops = env.current()->fees().base;
2044 BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
2045 chanBal = reqBal;
2046
2047 // claim again
2048 preBob = env.balance(bob);
2049 // A transaction that generates a tec still consumes its ticket.
2050 env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()),
2051 ticket::Use(bobTicketSeq++),
2052 Ter(tecUNFUNDED_PAYMENT));
2053
2054 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2055 BEAST_EXPECT(env.seq(bob) == bobSeq);
2056
2057 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2058 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2059 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
2060 }
2061 {
2062 // Try to claim more than authorized
2063 auto const preBob = env.balance(bob);
2064 STAmount const authAmt = chanBal + XRP(500);
2065 STAmount const reqAmt = authAmt + drops(1);
2066 assert(reqAmt <= chanAmt);
2067 // Note that since claim() returns a tem (neither tec nor tes),
2068 // the ticket is not consumed. So we don't kIncrement bobTicket.
2069 auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
2070 env(claim(bob, chan, reqAmt, authAmt, Slice(sig), alice.pk()),
2071 ticket::Use(bobTicketSeq),
2072 Ter(temBAD_AMOUNT));
2073
2074 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2075 BEAST_EXPECT(env.seq(bob) == bobSeq);
2076
2077 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2078 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2079 BEAST_EXPECT(env.balance(bob) == preBob);
2080 }
2081
2082 // Dst tries to fund the channel
2083 env(fund(bob, chan, XRP(1000)), ticket::Use(bobTicketSeq++), Ter(tecNO_PERMISSION));
2084
2085 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2086 BEAST_EXPECT(env.seq(bob) == bobSeq);
2087
2088 BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
2089 BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
2090
2091 {
2092 // Dst closes channel
2093 auto const preAlice = env.balance(alice);
2094 auto const preBob = env.balance(bob);
2095 env(claim(bob, chan), Txflags(tfClose), ticket::Use(bobTicketSeq++));
2096
2097 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2098 BEAST_EXPECT(env.seq(bob) == bobSeq);
2099
2100 BEAST_EXPECT(!channelExists(*env.current(), chan));
2101 auto const feeDrops = env.current()->fees().base;
2102 auto const delta = chanAmt - chanBal;
2103 assert(delta > beast::kZero);
2104 BEAST_EXPECT(env.balance(alice) == preAlice + delta);
2105 BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
2106 }
2107 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
2108 BEAST_EXPECT(env.seq(alice) == aliceSeq);
2109 env.require(tickets(bob, env.seq(bob) - bobTicketSeq));
2110 BEAST_EXPECT(env.seq(bob) == bobSeq);
2111 }
2112
2113 void
2115 {
2116 testSimple(features);
2117 testDisallowIncoming(features);
2118 testCancelAfter(features);
2119 testSettleDelay(features);
2120 testExpiration(features);
2121 testCloseDry(features);
2122 testDefaultAmount(features);
2123 testDisallowXRP(features);
2124 testDstTag(features);
2125 testDepositAuth(features);
2126 testMultiple(features);
2127 testAccountChannelsRPC(features);
2131 testAuthVerifyRPC(features);
2132 testOptionalFields(features);
2134 testMetaAndOwnership(features);
2135 testAccountDelete(features);
2136 testUsingTickets(features);
2137 testChannelVerifyLoadType(features);
2139 }
2140
2141public:
2142 void
2143 run() override
2144 {
2145 using namespace test::jtx;
2146 // fixCleanup3_2_0 changes payment-channel error codes (tem* -> tec*)
2147 // and channel-closing semantics. This suite asserts the
2148 // pre-amendment behavior, so run it with the amendment disabled.
2149 FeatureBitset const all{testableAmendments() - fixCleanup3_2_0};
2150 testWithFeats(all);
2152 testMetaAndOwnership(all - fixIncludeKeyletFields);
2153 }
2154};
2155
2157} // 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:271
std::shared_ptr< STLedgerEntry const > const & const_ref
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:45
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:1056
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:394
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:373
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
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
STAmount channelBalance(ReadView const &view, uint256 const &chan)
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)
uint256 channel(AccountID const &account, AccountID const &dst, std::uint32_t seqProxyValue)
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)
bool channelExists(ReadView const &view, uint256 const &chan)
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:16
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
void sign(json::Value &jv, Account const &account, json::Value &sigObject)
Sign automatically into a specific Json field of the jv object.
Definition utility.cpp:40
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 forAllApiVersions(Fn const &fn, Args &&... args)
Definition ApiVersion.h:159
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
void serializePayChanAuthorization(Serializer &msg, uint256 const &key, XRPAmount const &amt)
Definition PayChan.h:11
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
@ USER
Definition Role.h:27
@ ADMIN
Definition Role.h:27
@ 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:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
@ tecUNFUNDED_PAYMENT
Definition TER.h:288
@ tecNO_ENTRY
Definition TER.h:309
@ tecBAD_CREDENTIALS
Definition TER.h:362
@ tecEXPIRED
Definition TER.h:317
@ tecNO_PERMISSION
Definition TER.h:308
@ tecDST_TAG_NEEDED
Definition TER.h:312
@ tecHAS_OBLIGATIONS
Definition TER.h:320
@ tecNO_DST
Definition TER.h:293
@ tecUNFUNDED
Definition TER.h:298
json::Value doChannelAuthorize(rpc::JsonContext &context)
BaseUInt< 256 > uint256
Definition base_uint.h:580
@ tesSUCCESS
Definition TER.h:245
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)
static std::optional< std::int64_t > channelExpiration(ReadView const &view, uint256 const &chan)
void testWithFeats(FeatureBitset features)
void testDepositAuth(FeatureBitset features)
void testDefaultAmount(FeatureBitset features)
void testCloseDry(FeatureBitset features)
void testUsingTickets(FeatureBitset features)
void testDstTag(FeatureBitset features)
void testChannelAuthorizeLoadType(FeatureBitset features)
static std::pair< uint256, SLE::const_pointer > channelKeyAndSle(ReadView const &view, jtx::Account const &account, jtx::Account const &dst)
void testAccountChannelAuthorize(FeatureBitset features)
void testAccountChannelsRPC(FeatureBitset features)
void testExpiration(FeatureBitset features)
static Buffer signClaimAuth(PublicKey const &pk, SecretKey const &sk, uint256 const &channel, STAmount const &authAmt)
void testDisallowIncoming(FeatureBitset features)
void testChannelVerifyLoadType(FeatureBitset features)
void testAuthVerifyRPC(FeatureBitset features)
void testAccountDelete(FeatureBitset features)
static STAmount channelAmount(ReadView const &view, uint256 const &chan)
void run() override
Runs the suite.
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)