rippled
Loading...
Searching...
No Matches
XChain_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2022 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21#include <test/jtx/Env.h>
22#include <test/jtx/attester.h>
23#include <test/jtx/multisign.h>
24#include <test/jtx/xchain_bridge.h>
25
26#include <xrpl/beast/unit_test/suite.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/Indexes.h>
29#include <xrpl/protocol/Issue.h>
30#include <xrpl/protocol/SField.h>
31#include <xrpl/protocol/STXChainBridge.h>
32#include <xrpl/protocol/Serializer.h>
33#include <xrpl/protocol/TER.h>
34#include <xrpl/protocol/XChainAttestations.h>
35
36#include <functional>
37#include <limits>
38#include <optional>
39#include <random>
40#include <string>
41#include <tuple>
42#include <variant>
43#include <vector>
44
45namespace ripple::test {
46
47// SEnv class - encapsulate jtx::Env to make it more user-friendly,
48// for example having APIs that return a *this reference so that calls can be
49// chained (fluent interface) allowing to create an environment and use it
50// without encapsulating it in a curly brace block.
51// ---------------------------------------------------------------------------
52template <class T>
53struct SEnv
54{
56
58 T& s,
60 FeatureBitset features,
61 std::unique_ptr<Logs> logs = nullptr,
63 : env_(s, std::move(config), features, std::move(logs), thresh)
64 {
65 }
66
67 SEnv&
69 {
70 env_.close();
71 return *this;
72 }
73
74 SEnv&
75 enableFeature(uint256 const feature)
76 {
77 env_.enableFeature(feature);
78 return *this;
79 }
80
81 SEnv&
82 disableFeature(uint256 const feature)
83 {
84 env_.app().config().features.erase(feature);
85 return *this;
86 }
87
88 template <class Arg, class... Args>
89 SEnv&
90 fund(STAmount const& amount, Arg const& arg, Args const&... args)
91 {
92 env_.fund(amount, arg, args...);
93 return *this;
94 }
95
96 template <class JsonValue, class... FN>
97 SEnv&
98 tx(JsonValue&& jv, FN const&... fN)
99 {
100 env_(std::forward<JsonValue>(jv), fN...);
101 return *this;
102 }
103
104 template <class... FN>
105 SEnv&
106 multiTx(jtx::JValueVec&& jvv, FN const&... fN)
107 {
108 for (auto const& jv : jvv)
109 env_(jv, fN...);
110 return *this;
111 }
112
113 TER
114 ter() const
115 {
116 return env_.ter();
117 }
118
120 balance(jtx::Account const& account) const
121 {
122 return env_.balance(account).value();
123 }
124
126 balance(jtx::Account const& account, Issue const& issue) const
127 {
128 return env_.balance(account, issue).value();
129 }
130
133 {
134 return env_.current()->fees().accountReserve(count);
135 }
136
139 {
140 return env_.current()->fees().base;
141 }
142
144 account(jtx::Account const& account)
145 {
146 return env_.le(account);
147 }
148
150 bridge(Json::Value const& jvb)
151 {
152 STXChainBridge b(jvb);
153
154 auto tryGet =
156 if (auto r = env_.le(keylet::bridge(b, ct)))
157 {
158 if ((*r)[sfXChainBridge] == b)
159 return r;
160 }
161 return nullptr;
162 };
163 if (auto r = tryGet(STXChainBridge::ChainType::locking))
164 return r;
166 }
167
170 {
171 return (*bridge(jvb))[sfXChainAccountClaimCount];
172 }
173
176 {
177 return (*bridge(jvb))[sfXChainClaimID];
178 }
179
182 {
184 }
185
192};
193
194// XEnv class used for XChain tests. The only difference with SEnv<T> is that it
195// funds some default accounts, and that it enables `testable_amendments() |
196// FeatureBitset{featureXChainBridge}` by default.
197// -----------------------------------------------------------------------------
198template <class T>
199struct XEnv : public jtx::XChainBridgeObjects, public SEnv<T>
200{
201 XEnv(T& s, bool side = false) : SEnv<T>(s, jtx::envconfig(), features)
202 {
203 using namespace jtx;
204 STAmount xrp_funds{XRP(10000)};
205
206 if (!side)
207 {
208 this->fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw);
209
210 // Signer's list must match the attestation signers
211 // env_(jtx::signers(mcDoor, quorum, signers));
212 for (auto& s : signers)
213 this->fund(xrp_funds, s.account);
214 }
215 else
216 {
217 this->fund(
218 xrp_funds,
219 scDoor,
220 scAlice,
221 scBob,
222 scCarol,
223 scGw,
225 scReward);
226
227 for (auto& ra : payees)
228 this->fund(xrp_funds, ra);
229
230 for (auto& s : signers)
231 this->fund(xrp_funds, s.account);
232
233 // Signer's list must match the attestation signers
234 // env_(jtx::signers(Account::master, quorum, signers));
235 }
236 this->close();
237 }
238};
239
240// Tracks the xrp balance for one account
241template <class T>
243{
247
248 Balance(T& env, jtx::Account const& account) : account_(account), env_(env)
249 {
250 startAmount = env_.balance(account_);
251 }
252
254 diff() const
255 {
256 return env_.balance(account_) - startAmount;
257 }
258};
259
260// Tracks the xrp balance for multiple accounts involved in a crosss-chain
261// transfer
262template <class T>
264{
266
269 balance payor_; // pays the rewards
270 std::vector<balance> reward_accounts; // receives the reward
272
274 T& env,
275 jtx::Account const& from_acct,
276 jtx::Account const& to_acct,
277 jtx::Account const& payor,
278 jtx::Account const* payees,
279 size_t num_payees,
280 bool withClaim)
281 : from_(env, from_acct)
282 , to_(env, to_acct)
283 , payor_(env, payor)
284 , reward_accounts([&]() {
286 r.reserve(num_payees);
287 for (size_t i = 0; i < num_payees; ++i)
288 r.emplace_back(env, payees[i]);
289 return r;
290 }())
291 , txFees_(withClaim ? env.env_.current()->fees().base : XRPAmount(0))
292 {
293 }
294
296 T& env,
297 jtx::Account const& from_acct,
298 jtx::Account const& to_acct,
299 jtx::Account const& payor,
300 std::vector<jtx::Account> const& payees,
301 bool withClaim)
303 env,
304 from_acct,
305 to_acct,
306 payor,
307 &payees[0],
308 payees.size(),
309 withClaim)
310 {
311 }
312
313 bool
314 payees_received(STAmount const& reward) const
315 {
316 return std::all_of(
317 reward_accounts.begin(),
318 reward_accounts.end(),
319 [&](balance const& b) { return b.diff() == reward; });
320 }
321
322 bool
323 check_most_balances(STAmount const& amt, STAmount const& reward)
324 {
325 return from_.diff() == -amt && to_.diff() == amt &&
326 payees_received(reward);
327 }
328
329 bool
331 STAmount const& amt,
332 STAmount const& reward,
333 bool check_payer = true)
334 {
335 auto reward_cost =
336 multiply(reward, STAmount(reward_accounts.size()), reward.issue());
337 return check_most_balances(amt, reward) &&
338 (!check_payer || payor_.diff() == -(reward_cost + txFees_));
339 }
340
341 bool
343 {
344 return check_most_balances(STAmount(0), STAmount(0)) &&
345 payor_.diff() <= txFees_; // could have paid fee for failed claim
346 }
347};
348
350{
357 uint32_t quorum;
360
361 template <class ENV>
362 void
363 initBridge(ENV& mcEnv, ENV& scEnv)
364 {
366
367 auto const optAccountCreate = [&]() -> std::optional<STAmount> {
368 if (issueA != xrpIssue() || issueB != xrpIssue())
369 return {};
370 return minAccountCreate;
371 }();
372 mcEnv.tx(bridge_create(doorA, jvb, reward, optAccountCreate))
374 .close();
375
376 scEnv.tx(bridge_create(doorB, jvb, reward, optAccountCreate))
378 .close();
379 }
380};
381
384{
387 {
388 return XEnv(*this).env_.current()->fees().accountReserve(count);
389 }
390
393 {
394 return XEnv(*this).env_.current()->fees().base;
395 }
396
397 void
399 {
400 auto jBridge = create_bridge(mcDoor)[sfXChainBridge.jsonName];
401 bool exceptionPresent = false;
402 try
403 {
404 exceptionPresent = false;
405 [[maybe_unused]] STXChainBridge testBridge1(jBridge);
406 }
407 catch (std::exception& ec)
408 {
409 exceptionPresent = true;
410 }
411
412 BEAST_EXPECT(!exceptionPresent);
413
414 try
415 {
416 exceptionPresent = false;
417 jBridge["Extra"] = 1;
418 [[maybe_unused]] STXChainBridge testBridge2(jBridge);
419 }
420 catch ([[maybe_unused]] std::exception& ec)
421 {
422 exceptionPresent = true;
423 }
424
425 BEAST_EXPECT(exceptionPresent);
426 }
427
428 void
430 {
431 XRPAmount res1 = reserve(1);
432
433 using namespace jtx;
434 testcase("Create Bridge");
435
436 // Normal create_bridge => should succeed
437 XEnv(*this).tx(create_bridge(mcDoor)).close();
438
439 // Bridge not owned by one of the door account.
440 XEnv(*this).tx(
442
443 // Create twice on the same account
444 XEnv(*this)
446 .close()
448
449 // Create USD bridge Alice -> Bob ... should succeed
450 XEnv(*this).tx(
452 mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
453 ter(tesSUCCESS));
454
455 // Create USD bridge, Alice is both the locking door and locking issue,
456 // ... should fail.
457 XEnv(*this).tx(
459 mcAlice, bridge(mcAlice, mcAlice["USD"], mcBob, mcBob["USD"])),
461
462 // Bridge where the two door accounts are equal.
463 XEnv(*this).tx(
465 mcBob, bridge(mcBob, mcGw["USD"], mcBob, mcGw["USD"])),
467
468 // Both door accounts are on the same chain. This is not allowed.
469 // Although it doesn't violate any invariants, it's not a useful thing
470 // to do and it complicates the "add claim" transactions.
471 XEnv(*this)
473 mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])))
474 .close()
476 mcBob, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
478 .close();
479
480 // Create a bridge on an account with exactly enough balance to
481 // meet the new reserve should succeed
482 XEnv(*this)
483 .fund(res1, mcuDoor) // exact reserve for account + 1 object
484 .close()
486
487 // Create a bridge on an account with no enough balance to meet the
488 // new reserve
489 XEnv(*this)
490 .fund(res1 - 1, mcuDoor) // just short of required reserve
491 .close()
493
494 // Reward amount is non-xrp
495 XEnv(*this).tx(
498
499 // Reward amount is XRP and negative
500 XEnv(*this).tx(
503
504 // Reward amount is 1 xrp => should succeed
506
507 // Min create amount is 1 xrp, mincreate is 1 xrp => should succeed
508 XEnv(*this).tx(
510
511 // Min create amount is non-xrp
512 XEnv(*this).tx(
513 create_bridge(mcDoor, jvb, XRP(1), mcUSD(100)),
515
516 // Min create amount is zero (should fail, currently succeeds)
517 XEnv(*this).tx(
518 create_bridge(mcDoor, jvb, XRP(1), XRP(0)),
520
521 // Min create amount is negative
522 XEnv(*this).tx(
523 create_bridge(mcDoor, jvb, XRP(1), XRP(-1)),
525
526 // coverage test: BridgeCreate::preflight() - create bridge when feature
527 // disabled.
528 {
529 Env env(*this, testable_amendments() - featureXChainBridge);
531 }
532
533 // coverage test: BridgeCreate::preclaim() returns tecNO_ISSUER.
534 XEnv(*this).tx(
536 mcAlice, bridge(mcAlice, mcuAlice["USD"], mcBob, mcBob["USD"])),
538
539 // coverage test: create_bridge transaction with incorrect flag
540 XEnv(*this).tx(
544
545 // coverage test: create_bridge transaction with xchain feature disabled
546 XEnv(*this)
547 .disableFeature(featureXChainBridge)
549 }
550
551 void
553 {
599 using namespace jtx;
600 testcase("Bridge create constraints");
601 XEnv env(*this, true);
602 auto& A = scAlice;
603 auto& B = scBob;
604 auto& C = scCarol;
605 auto AUSD = A["USD"];
606 auto BUSD = B["USD"];
607 auto CUSD = C["USD"];
608 auto GUSD = scGw["USD"];
609 auto AEUR = A["EUR"];
610 auto BEUR = B["EUR"];
611 auto CEUR = C["EUR"];
612 auto GEUR = scGw["EUR"];
613
614 // Accounts to own single brdiges
615 Account const a1("a1");
616 Account const a2("a2");
617 Account const a3("a3");
618 Account const a4("a4");
619 Account const a5("a5");
620 Account const a6("a6");
621
622 env.fund(XRP(10000), a1, a2, a3, a4, a5, a6);
623 env.close();
624
625 // Add a bridge on two different accounts with the same locking and
626 // issuing assets
627 env.tx(create_bridge(a1, bridge(a1, GUSD, B, BUSD))).close();
628 env.tx(create_bridge(a2, bridge(a2, GUSD, B, BUSD))).close();
629
630 // Add the exact same bridge to two different accounts (one locking
631 // account and one issuing)
632 env.tx(create_bridge(a3, bridge(a3, GUSD, a4, a4["USD"]))).close();
633 env.tx(create_bridge(a4, bridge(a3, GUSD, a4, a4["USD"])),
635 .close();
636
637 // Add the exact same bridge to two different accounts (one issuing
638 // account and one locking - opposite order from the test above)
639 env.tx(create_bridge(a5, bridge(a6, GUSD, a5, a5["USD"]))).close();
640 env.tx(create_bridge(a6, bridge(a6, GUSD, a5, a5["USD"])),
642 .close();
643
644 // Test case 1 ~ 5, create bridges
645 auto const goodBridge1 = bridge(A, GUSD, B, BUSD);
646 auto const goodBridge2 = bridge(A, BUSD, C, CUSD);
647 env.tx(create_bridge(B, goodBridge1)).close();
648 // Issuing asset is the same, this is a duplicate
649 env.tx(create_bridge(B, bridge(A, GEUR, B, BUSD)), ter(tecDUPLICATE))
650 .close();
651 env.tx(create_bridge(A, goodBridge2), ter(tesSUCCESS)).close();
652 // Locking asset is the same - this is a duplicate
653 env.tx(create_bridge(A, bridge(A, BUSD, B, BEUR)), ter(tecDUPLICATE))
654 .close();
655 // Locking asset is USD - this is a duplicate even tho it has a
656 // different issuer
657 env.tx(create_bridge(A, bridge(A, CUSD, B, BEUR)), ter(tecDUPLICATE))
658 .close();
659
660 // Test case 6 and 7, commits
661 env.tx(trust(C, BUSD(1000)))
662 .tx(trust(A, BUSD(1000)))
663 .close()
664 .tx(pay(B, C, BUSD(1000)))
665 .close();
666 auto const aBalanceStart = env.balance(A, BUSD);
667 auto const cBalanceStart = env.balance(C, BUSD);
668 env.tx(xchain_commit(C, goodBridge1, 1, BUSD(50))).close();
669 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(0));
670 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50));
671 env.tx(xchain_commit(C, goodBridge2, 1, BUSD(60))).close();
672 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(60));
673 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50 - 60));
674
675 // bridge modify test cases
676 env.tx(bridge_modify(B, goodBridge1, XRP(33), std::nullopt)).close();
677 BEAST_EXPECT((*env.bridge(goodBridge1))[sfSignatureReward] == XRP(33));
678 env.tx(bridge_modify(A, goodBridge2, XRP(44), std::nullopt)).close();
679 BEAST_EXPECT((*env.bridge(goodBridge2))[sfSignatureReward] == XRP(44));
680 }
681
682 void
684 {
685 using namespace jtx;
686 testcase("Create Bridge Matrix");
687
688 // Test all combinations of the following:`
689 // --------------------------------------
690 // - Locking chain is IOU with locking chain door account as issuer
691 // - Locking chain is IOU with issuing chain door account that
692 // exists on the locking chain as issuer
693 // - Locking chain is IOU with issuing chain door account that does
694 // not exists on the locking chain as issuer
695 // - Locking chain is IOU with non-door account (that exists on the
696 // locking chain ledger) as issuer
697 // - Locking chain is IOU with non-door account (that does not exist
698 // exists on the locking chain ledger) as issuer
699 // - Locking chain is XRP
700 // ---------------------------------------------------------------------
701 // - Issuing chain is IOU with issuing chain door account as the
702 // issuer
703 // - Issuing chain is IOU with locking chain door account (that
704 // exists on the issuing chain ledger) as the issuer
705 // - Issuing chain is IOU with locking chain door account (that does
706 // not exist on the issuing chain ledger) as the issuer
707 // - Issuing chain is IOU with non-door account (that exists on the
708 // issuing chain ledger) as the issuer
709 // - Issuing chain is IOU with non-door account (that does not
710 // exists on the issuing chain ledger) as the issuer
711 // - Issuing chain is XRP and issuing chain door account is not the
712 // root account
713 // - Issuing chain is XRP and issuing chain door account is the root
714 // account
715 // ---------------------------------------------------------------------
716 // That's 42 combinations. The only combinations that should succeed
717 // are:
718 // - Locking chain is any IOU,
719 // - Issuing chain is IOU with issuing chain door account as the
720 // issuer
721 // Locking chain is XRP,
722 // - Issuing chain is XRP with issuing chain is the root account.
723 // ---------------------------------------------------------------------
724 Account a("a"), b("b");
725 Issue ia, ib;
726
727 std::tuple lcs{
729 "Locking chain is IOU(locking chain door)",
730 [&](auto& env, bool) {
731 a = mcDoor;
732 ia = mcDoor["USD"];
733 }),
735 "Locking chain is IOU(issuing chain door funded on locking "
736 "chain)",
737 [&](auto& env, bool shouldFund) {
738 a = mcDoor;
739 ia = scDoor["USD"];
740 if (shouldFund)
741 env.fund(XRP(10000), scDoor);
742 }),
744 "Locking chain is IOU(issuing chain door account unfunded "
745 "on locking chain)",
746 [&](auto& env, bool) {
747 a = mcDoor;
748 ia = scDoor["USD"];
749 }),
751 "Locking chain is IOU(bob funded on locking chain)",
752 [&](auto& env, bool) {
753 a = mcDoor;
754 ia = mcGw["USD"];
755 }),
757 "Locking chain is IOU(bob unfunded on locking chain)",
758 [&](auto& env, bool) {
759 a = mcDoor;
760 ia = mcuGw["USD"];
761 }),
762 std::make_pair("Locking chain is XRP", [&](auto& env, bool) {
763 a = mcDoor;
764 ia = xrpIssue();
765 })};
766
767 std::tuple ics{
769 "Issuing chain is IOU(issuing chain door account)",
770 [&](auto& env, bool) {
771 b = scDoor;
772 ib = scDoor["USD"];
773 }),
775 "Issuing chain is IOU(locking chain door funded on issuing "
776 "chain)",
777 [&](auto& env, bool shouldFund) {
778 b = scDoor;
779 ib = mcDoor["USD"];
780 if (shouldFund)
781 env.fund(XRP(10000), mcDoor);
782 }),
784 "Issuing chain is IOU(locking chain door unfunded on "
785 "issuing chain)",
786 [&](auto& env, bool) {
787 b = scDoor;
788 ib = mcDoor["USD"];
789 }),
791 "Issuing chain is IOU(bob funded on issuing chain)",
792 [&](auto& env, bool) {
793 b = scDoor;
794 ib = mcGw["USD"];
795 }),
797 "Issuing chain is IOU(bob unfunded on issuing chain)",
798 [&](auto& env, bool) {
799 b = scDoor;
800 ib = mcuGw["USD"];
801 }),
803 "Issuing chain is XRP and issuing chain door account is "
804 "not the root account",
805 [&](auto& env, bool) {
806 b = scDoor;
807 ib = xrpIssue();
808 }),
810 "Issuing chain is XRP and issuing chain door account is "
811 "the root account ",
812 [&](auto& env, bool) {
813 b = Account::master;
814 ib = xrpIssue();
815 })};
816
817 std::vector<std::pair<int, int>> expected_result{
860
862
863 auto testcase = [&](auto const& lc, auto const& ic) {
864 XEnv mcEnv(*this);
865 XEnv scEnv(*this, true);
866
867 lc.second(mcEnv, true);
868 lc.second(scEnv, false);
869
870 ic.second(mcEnv, false);
871 ic.second(scEnv, true);
872
873 auto const& expected = expected_result[test_result.size()];
874
875 mcEnv.tx(
876 create_bridge(a, bridge(a, ia, b, ib)),
877 ter(TER::fromInt(expected.first)));
878 TER mcTER = mcEnv.env_.ter();
879
880 scEnv.tx(
881 create_bridge(b, bridge(a, ia, b, ib)),
882 ter(TER::fromInt(expected.second)));
883 TER scTER = scEnv.env_.ter();
884
885 bool pass = mcTER == tesSUCCESS && scTER == tesSUCCESS;
886
887 test_result.emplace_back(mcTER, scTER, pass);
888 };
889
890 auto apply_ics = [&](auto const& lc, auto const& ics) {
892 [&](auto const&... ic) { (testcase(lc, ic), ...); }, ics);
893 };
894
895 std::apply([&](auto const&... lc) { (apply_ics(lc, ics), ...); }, lcs);
896
897#if GENERATE_MTX_OUTPUT
898 // optional output of matrix results in markdown format
899 // ----------------------------------------------------
900 std::string fname{std::tmpnam(nullptr)};
901 fname += ".md";
902 std::cout << "Markdown output for matrix test: " << fname << "\n";
903
904 auto print_res = [](auto tup) -> std::string {
906 " / " + transToken(std::get<1>(tup));
907
908 if (std::get<2>(tup))
909 return status;
910 else
911 {
912 // red
913 return std::string("`") + status + "`";
914 }
915 };
916
917 auto output_table = [&](auto print_res) {
918 size_t test_idx = 0;
919 std::string res;
920 res.reserve(10000); // should be enough :-)
921
922 // first two header lines
923 res += "| `issuing ->` | ";
925 [&](auto const&... ic) {
926 ((res += ic.first, res += " | "), ...);
927 },
928 ics);
929 res += "\n";
930
931 res += "| :--- | ";
933 [&](auto const&... ic) {
934 (((void)ic.first, res += ":---: | "), ...);
935 },
936 ics);
937 res += "\n";
938
939 auto output = [&](auto const& lc, auto const& ic) {
940 res += print_res(test_result[test_idx]);
941 res += " | ";
942 ++test_idx;
943 };
944
945 auto output_ics = [&](auto const& lc, auto const& ics) {
946 res += "| ";
947 res += lc.first;
948 res += " | ";
950 [&](auto const&... ic) { (output(lc, ic), ...); }, ics);
951 res += "\n";
952 };
953
955 [&](auto const&... lc) { (output_ics(lc, ics), ...); }, lcs);
956
957 return res;
958 };
959
960 std::ofstream(fname) << output_table(print_res);
961
962 std::string ter_fname{std::tmpnam(nullptr)};
963 std::cout << "ter output for matrix test: " << ter_fname << "\n";
964
965 std::ofstream ofs(ter_fname);
966 for (auto& t : test_result)
967 {
968 ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", "
969 << std::string(transToken(std::get<1>(t))) << "}\n,";
970 }
971#endif
972 }
973
974 void
976 {
977 using namespace jtx;
978 testcase("Modify Bridge");
979
980 // Changing a non-existent bridge should fail
981 XEnv(*this).tx(
983 mcAlice,
984 bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]),
985 XRP(2),
988
989 // must change something
990 // XEnv(*this)
991 // .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
992 // .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(1)),
993 // ter(temMALFORMED));
994
995 // must change something
996 XEnv(*this)
997 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
998 .close()
1000
1001 // Reward amount is non-xrp
1002 XEnv(*this).tx(
1003 bridge_modify(mcDoor, jvb, mcUSD(2), XRP(10)),
1005
1006 // Reward amount is XRP and negative
1007 XEnv(*this).tx(
1008 bridge_modify(mcDoor, jvb, XRP(-2), XRP(10)),
1010
1011 // Min create amount is non-xrp
1012 XEnv(*this).tx(
1013 bridge_modify(mcDoor, jvb, XRP(2), mcUSD(10)),
1015
1016 // Min create amount is zero
1017 XEnv(*this).tx(
1018 bridge_modify(mcDoor, jvb, XRP(2), XRP(0)),
1020
1021 // Min create amount is negative
1022 XEnv(*this).tx(
1023 bridge_modify(mcDoor, jvb, XRP(2), XRP(-10)),
1025
1026 // First check the regular claim process (without bridge_modify)
1027 for (auto withClaim : {false, true})
1028 {
1029 XEnv mcEnv(*this);
1030 XEnv scEnv(*this, true);
1031
1032 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1033
1036 .close()
1038 .close();
1039
1040 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1041 auto const amt = XRP(1000);
1042 std::uint32_t const claimID = 1;
1043 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1044
1045 BalanceTransfer transfer(
1046 scEnv,
1048 scBob,
1049 scAlice,
1050 &payees[0],
1052 withClaim);
1053
1054 scEnv
1056 scAttester,
1057 jvb,
1058 mcAlice,
1059 amt,
1060 payees,
1061 true,
1062 claimID,
1063 dst,
1064 signers))
1065 .close();
1066
1067 if (withClaim)
1068 {
1069 BEAST_EXPECT(transfer.has_not_happened());
1070
1071 // need to submit a claim transactions
1072 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1073 .close();
1074 }
1075
1076 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1077 }
1078
1079 // Check that the reward paid from a claim Id was the reward when
1080 // the claim id was created, not the reward since the bridge was
1081 // modified.
1082 for (auto withClaim : {false, true})
1083 {
1084 XEnv mcEnv(*this);
1085 XEnv scEnv(*this, true);
1086
1087 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1088
1091 .close()
1093 .close();
1094
1095 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1096 auto const amt = XRP(1000);
1097 std::uint32_t const claimID = 1;
1098 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1099
1100 // Now modify the reward on the bridge
1101 mcEnv.tx(bridge_modify(mcDoor, jvb, XRP(2), XRP(10))).close();
1102 scEnv.tx(bridge_modify(Account::master, jvb, XRP(2), XRP(10)))
1103 .close();
1104
1105 BalanceTransfer transfer(
1106 scEnv,
1108 scBob,
1109 scAlice,
1110 &payees[0],
1112 withClaim);
1113
1114 scEnv
1116 scAttester,
1117 jvb,
1118 mcAlice,
1119 amt,
1120 payees,
1121 true,
1122 claimID,
1123 dst,
1124 signers))
1125 .close();
1126
1127 if (withClaim)
1128 {
1129 BEAST_EXPECT(transfer.has_not_happened());
1130
1131 // need to submit a claim transactions
1132 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1133 .close();
1134 }
1135
1136 // make sure the reward accounts indeed received the original
1137 // split reward (1 split 5 ways) instead of the updated 2 XRP.
1138 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1139 }
1140
1141 // Check that the signatures used to verify attestations and decide
1142 // if there is a quorum are the current signer's list on the door
1143 // account, not the signer's list that was in effect when the claim
1144 // id was created.
1145 for (auto withClaim : {false, true})
1146 {
1147 XEnv mcEnv(*this);
1148 XEnv scEnv(*this, true);
1149
1150 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1151
1154 .close()
1156 .close();
1157
1158 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1159 auto const amt = XRP(1000);
1160 std::uint32_t const claimID = 1;
1161 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1162
1163 // change signers - claim should not be processed is the batch
1164 // is signed by original signers
1166 .close();
1167
1168 BalanceTransfer transfer(
1169 scEnv,
1171 scBob,
1172 scAlice,
1173 &payees[0],
1175 withClaim);
1176
1177 // submit claim using outdated signers - should fail
1178 scEnv
1179 .multiTx(
1181 scAttester,
1182 jvb,
1183 mcAlice,
1184 amt,
1185 payees,
1186 true,
1187 claimID,
1188 dst,
1189 signers),
1191 .close();
1192 if (withClaim)
1193 {
1194 // need to submit a claim transactions
1195 scEnv
1196 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1198 .close();
1199 }
1200
1201 // make sure transfer has not happened as we sent attestations
1202 // using outdated signers
1203 BEAST_EXPECT(transfer.has_not_happened());
1204
1205 // submit claim using current signers - should succeed
1206 scEnv
1208 scAttester,
1209 jvb,
1210 mcAlice,
1211 amt,
1212 payees,
1213 true,
1214 claimID,
1215 dst,
1216 alt_signers))
1217 .close();
1218 if (withClaim)
1219 {
1220 BEAST_EXPECT(transfer.has_not_happened());
1221
1222 // need to submit a claim transactions
1223 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1224 .close();
1225 }
1226
1227 // make sure the transfer went through as we sent attestations
1228 // using new signers
1229 BEAST_EXPECT(
1230 transfer.has_happened(amt, split_reward_quorum, false));
1231 }
1232
1233 // coverage test: bridge_modify transaction with incorrect flag
1234 XEnv(*this)
1236 .close()
1237 .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)),
1240
1241 // coverage test: bridge_modify transaction with xchain feature
1242 // disabled
1243 XEnv(*this)
1245 .disableFeature(featureXChainBridge)
1246 .close()
1248
1249 // coverage test: bridge_modify return temSIDECHAIN_NONDOOR_OWNER;
1250 XEnv(*this)
1252 .close()
1253 .tx(bridge_modify(mcAlice, jvb, XRP(1), XRP(2)),
1255
1262 XEnv(*this)
1263 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20)))
1264 .close()
1266 mcAlice, jvb, scuAlice, XRP(100), reward))
1267 .close()
1268 .tx(bridge_modify(mcDoor, jvb, {}, XRP(2)),
1271 .close()
1272 .tx(bridge_modify(mcDoor, jvb, XRP(3), {}),
1274 .close()
1276 mcAlice, jvb, scuBob, XRP(100), XRP(3)),
1278 .close();
1279 }
1280
1281 void
1283 {
1284 using namespace jtx;
1285 XRPAmount res1 = reserve(1);
1286 XRPAmount tx_fee = txFee();
1287
1288 testcase("Create ClaimID");
1289
1290 // normal bridge create for sanity check with the exact necessary
1291 // account balance
1292 XEnv(*this, true)
1294 .fund(res1, scuAlice) // acct reserve + 1 object
1295 .close()
1297 .close();
1298
1299 // check reward not deducted when claim id is created
1300 {
1301 XEnv xenv(*this, true);
1302
1303 Balance scAlice_bal(xenv, scAlice);
1304
1307 .close();
1308
1309 BEAST_EXPECT(scAlice_bal.diff() == -tx_fee);
1310 }
1311
1312 // Non-existent bridge
1313 XEnv(*this, true)
1315 scAlice,
1316 bridge(mcAlice, mcAlice["USD"], scBob, scBob["USD"]),
1317 reward,
1318 mcAlice),
1320 .close();
1321
1322 // Creating the new object would put the account below the reserve
1323 XEnv(*this, true)
1325 .fund(res1 - xrp_dust, scuAlice) // barely not enough
1326 .close()
1329 .close();
1330
1331 // The specified reward doesn't match the reward on the bridge (test
1332 // by giving the reward amount for the other side, as well as a
1333 // completely non-matching reward)
1334 XEnv(*this, true)
1336 .close()
1340 .close();
1341
1342 // A reward amount that isn't XRP
1343 XEnv(*this, true)
1345 .close()
1348 .close();
1349
1350 // coverage test: xchain_create_claim_id transaction with incorrect
1351 // flag
1352 XEnv(*this, true)
1354 .close()
1358 .close();
1359
1360 // coverage test: xchain_create_claim_id transaction with xchain
1361 // feature disabled
1362 XEnv(*this, true)
1364 .disableFeature(featureXChainBridge)
1365 .close()
1368 .close();
1369 }
1370
1371 void
1373 {
1374 using namespace jtx;
1375 XRPAmount res0 = reserve(0);
1376 XRPAmount tx_fee = txFee();
1377
1378 testcase("Commit");
1379
1380 // Commit to a non-existent bridge
1381 XEnv(*this).tx(
1383
1384 // check that reward not deducted when doing the commit
1385 {
1386 XEnv xenv(*this);
1387
1388 Balance alice_bal(xenv, mcAlice);
1389 auto const amt = XRP(1000);
1390
1391 xenv.tx(create_bridge(mcDoor, jvb))
1392 .close()
1393 .tx(xchain_commit(mcAlice, jvb, 1, amt, scBob))
1394 .close();
1395
1396 STAmount claim_cost = amt;
1397 BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee));
1398 }
1399
1400 // Commit a negative amount
1401 XEnv(*this)
1403 .close()
1404 .tx(xchain_commit(mcAlice, jvb, 1, XRP(-1), scBob),
1406
1407 // Commit an amount whose issue that does not match the expected
1408 // issue on the bridge (either LockingChainIssue or
1409 // IssuingChainIssue, depending on the chain).
1410 XEnv(*this)
1412 .close()
1413 .tx(xchain_commit(mcAlice, jvb, 1, mcUSD(100), scBob),
1415
1416 // Commit an amount that would put the sender below the required
1417 // reserve (if XRP)
1418 XEnv(*this)
1420 .fund(res0 + one_xrp - xrp_dust, mcuAlice) // barely not enough
1421 .close()
1424
1425 XEnv(*this)
1427 .fund(
1428 res0 + one_xrp + xrp_dust, // "xrp_dust" for tx fees
1429 mcuAlice) // exactly enough => should succeed
1430 .close()
1432
1433 // Commit an amount above the account's balance (for both XRP and
1434 // IOUs)
1435 XEnv(*this)
1437 .fund(res0, mcuAlice) // barely not enough
1438 .close()
1439 .tx(xchain_commit(mcuAlice, jvb, 1, res0 + one_xrp, scBob),
1441
1442 auto jvb_USD = bridge(mcDoor, mcUSD, scGw, scUSD);
1443
1444 // commit sent from iou issuer (mcGw) succeeds - should it?
1445 XEnv(*this)
1446 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1447 .tx(create_bridge(mcDoor, jvb_USD))
1448 .close()
1449 .tx(xchain_commit(mcGw, jvb_USD, 1, mcUSD(1), scBob));
1450
1451 // commit to a door account from the door account. This should fail.
1452 XEnv(*this)
1453 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1454 .tx(create_bridge(mcDoor, jvb_USD))
1455 .close()
1456 .tx(xchain_commit(mcDoor, jvb_USD, 1, mcUSD(1), scBob),
1458
1459 // commit sent from mcAlice which has no IOU balance => should fail
1460 XEnv(*this)
1461 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1462 .tx(create_bridge(mcDoor, jvb_USD))
1463 .close()
1464 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scBob),
1465 ter(terNO_LINE));
1466
1467 // commit sent from mcAlice which has no IOU balance => should fail
1468 // just changed the destination to scGw (which is the door account
1469 // and may not make much sense)
1470 XEnv(*this)
1471 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1472 .tx(create_bridge(mcDoor, jvb_USD))
1473 .close()
1474 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scGw),
1475 ter(terNO_LINE));
1476
1477 // commit sent from mcAlice which has a IOU balance => should
1478 // succeed
1479 XEnv(*this)
1480 .tx(trust(mcDoor, mcUSD(10000)))
1481 .tx(trust(mcAlice, mcUSD(10000)))
1482 .close()
1483 .tx(pay(mcGw, mcAlice, mcUSD(10)))
1484 .tx(create_bridge(mcDoor, jvb_USD))
1485 .close()
1486 //.tx(pay(mcAlice, mcDoor, mcUSD(10)));
1487 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(10), scAlice));
1488
1489 // coverage test: xchain_commit transaction with incorrect flag
1490 XEnv(*this)
1492 .close()
1496
1497 // coverage test: xchain_commit transaction with xchain feature
1498 // disabled
1499 XEnv(*this)
1501 .disableFeature(featureXChainBridge)
1502 .close()
1504 ter(temDISABLED));
1505 }
1506
1507 void
1509 {
1510 using namespace jtx;
1511
1512 testcase("Add Attestation");
1513 XRPAmount res0 = reserve(0);
1514 XRPAmount tx_fee = txFee();
1515
1516 auto multiTtxFee = [&](std::uint32_t m) -> STAmount {
1517 return multiply(tx_fee, STAmount(m), xrpIssue());
1518 };
1519
1520 // Add an attestation to a claim id that has already reached quorum.
1521 // This should succeed and share in the reward.
1522 // note: this is true only when either:
1523 // 1. dest account is not specified, so transfer requires a claim
1524 // 2. or the extra attestation is sent in the same batch as the
1525 // one reaching quorum
1526 for (auto withClaim : {true})
1527 {
1528 XEnv mcEnv(*this);
1529 XEnv scEnv(*this, true);
1530 std::uint32_t const claimID = 1;
1531
1532 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1533
1536 .close()
1538 .close();
1539
1540 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1541
1542 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1543 auto const amt = XRP(1000);
1544 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1545
1546 BalanceTransfer transfer(
1547 scEnv, Account::master, scBob, scAlice, payees, withClaim);
1548
1549 scEnv
1551 scAttester,
1552 jvb,
1553 mcAlice,
1554 amt,
1555 payees,
1556 true,
1557 claimID,
1558 dst,
1559 signers,
1561 .close();
1562 scEnv
1564 scAttester,
1565 jvb,
1566 mcAlice,
1567 amt,
1569 true,
1570 claimID,
1571 dst,
1573 .close();
1574
1575 if (withClaim)
1576 {
1577 BEAST_EXPECT(transfer.has_not_happened());
1578
1579 // need to submit a claim transactions
1580 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1581 .close();
1582 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1583 BEAST_EXPECT(scEnv.claimID(jvb) == claimID);
1584 }
1585
1586 BEAST_EXPECT(transfer.has_happened(amt, split_reward_everyone));
1587 }
1588
1589 // Test that signature weights are correctly handled. Assign
1590 // signature weights of 1,2,4,4 and a quorum of 7. Check that the
1591 // 4,4 signatures reach a quorum, the 1,2,4, reach a quorum, but the
1592 // 4,2, 4,1 and 1,2 do not.
1593
1594 // 1,2,4 => should succeed
1595 for (auto withClaim : {false, true})
1596 {
1597 XEnv mcEnv(*this);
1598 XEnv scEnv(*this, true);
1599
1600 std::uint32_t const quorum_7 = 7;
1601 std::vector<signer> const signers_ = [] {
1602 constexpr int numSigners = 4;
1603 std::uint32_t weights[] = {1, 2, 4, 4};
1604
1605 std::vector<signer> result;
1606 result.reserve(numSigners);
1607 for (int i = 0; i < numSigners; ++i)
1608 {
1609 using namespace std::literals;
1610 auto const a = Account("signer_"s + std::to_string(i));
1611 result.emplace_back(a, weights[i]);
1612 }
1613 return result;
1614 }();
1615
1616 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1617
1619 .tx(jtx::signers(Account::master, quorum_7, signers_))
1620 .close()
1622 .close();
1623 std::uint32_t const claimID = 1;
1624 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1625
1626 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1627 auto const amt = XRP(1000);
1628
1629 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1630
1631 BalanceTransfer transfer(
1632 scEnv,
1634 scBob,
1635 scAlice,
1636 &payees[0],
1637 3,
1638 withClaim);
1639
1640 scEnv
1642 scAttester,
1643 jvb,
1644 mcAlice,
1645 amt,
1646 payees,
1647 true,
1648 claimID,
1649 dst,
1650 signers_,
1651 3))
1652 .close();
1653
1654 if (withClaim)
1655 {
1656 BEAST_EXPECT(transfer.has_not_happened());
1657
1658 // need to submit a claim transactions
1659 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1660 .close();
1661 }
1662
1663 BEAST_EXPECT(!scEnv.claimID(jvb, 1)); // claim id deleted
1664
1665 BEAST_EXPECT(transfer.has_happened(
1666 amt, divide(reward, STAmount(3), reward.issue())));
1667 }
1668
1669 // 4,4 => should succeed
1670 for (auto withClaim : {false, true})
1671 {
1672 XEnv mcEnv(*this);
1673 XEnv scEnv(*this, true);
1674
1675 std::uint32_t const quorum_7 = 7;
1676 std::vector<signer> const signers_ = [] {
1677 constexpr int numSigners = 4;
1678 std::uint32_t weights[] = {1, 2, 4, 4};
1679
1680 std::vector<signer> result;
1681 result.reserve(numSigners);
1682 for (int i = 0; i < numSigners; ++i)
1683 {
1684 using namespace std::literals;
1685 auto const a = Account("signer_"s + std::to_string(i));
1686 result.emplace_back(a, weights[i]);
1687 }
1688 return result;
1689 }();
1690 STAmount const split_reward_ =
1691 divide(reward, STAmount(signers_.size()), reward.issue());
1692
1693 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1694
1696 .tx(jtx::signers(Account::master, quorum_7, signers_))
1697 .close()
1699 .close();
1700 std::uint32_t const claimID = 1;
1701 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1702
1703 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1704 auto const amt = XRP(1000);
1705
1706 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1707
1708 BalanceTransfer transfer(
1709 scEnv,
1711 scBob,
1712 scAlice,
1713 &payees[2],
1714 2,
1715 withClaim);
1716
1717 scEnv
1719 scAttester,
1720 jvb,
1721 mcAlice,
1722 amt,
1723 payees,
1724 true,
1725 claimID,
1726 dst,
1727 signers_,
1728 2,
1729 2))
1730 .close();
1731
1732 if (withClaim)
1733 {
1734 BEAST_EXPECT(transfer.has_not_happened());
1735
1736 // need to submit a claim transactions
1737 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1738 .close();
1739 }
1740
1741 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1742
1743 BEAST_EXPECT(transfer.has_happened(
1744 amt, divide(reward, STAmount(2), reward.issue())));
1745 }
1746
1747 // 1,2 => should fail
1748 for (auto withClaim : {false, true})
1749 {
1750 XEnv mcEnv(*this);
1751 XEnv scEnv(*this, true);
1752
1753 std::uint32_t const quorum_7 = 7;
1754 std::vector<signer> const signers_ = [] {
1755 constexpr int numSigners = 4;
1756 std::uint32_t weights[] = {1, 2, 4, 4};
1757
1758 std::vector<signer> result;
1759 result.reserve(numSigners);
1760 for (int i = 0; i < numSigners; ++i)
1761 {
1762 using namespace std::literals;
1763 auto const a = Account("signer_"s + std::to_string(i));
1764 result.emplace_back(a, weights[i]);
1765 }
1766 return result;
1767 }();
1768
1769 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1770
1772 .tx(jtx::signers(Account::master, quorum_7, signers_))
1773 .close()
1775 .close();
1776
1777 std::uint32_t const claimID = 1;
1778 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1779
1780 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1781 auto const amt = XRP(1000);
1782 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1783
1784 BalanceTransfer transfer(
1785 scEnv,
1787 scBob,
1788 scAlice,
1789 &payees[0],
1790 2,
1791 withClaim);
1792
1793 scEnv
1795 scAttester,
1796 jvb,
1797 mcAlice,
1798 amt,
1799 payees,
1800 true,
1801 claimID,
1802 dst,
1803 signers_,
1804 2))
1805 .close();
1806 if (withClaim)
1807 {
1808 BEAST_EXPECT(transfer.has_not_happened());
1809
1810 // need to submit a claim transactions
1811 scEnv
1812 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1814 .close();
1815 }
1816
1817 BEAST_EXPECT(
1818 !!scEnv.claimID(jvb, claimID)); // claim id still present
1819 BEAST_EXPECT(transfer.has_not_happened());
1820 }
1821
1822 // 2,4 => should fail
1823 for (auto withClaim : {false, true})
1824 {
1825 XEnv mcEnv(*this);
1826 XEnv scEnv(*this, true);
1827
1828 std::uint32_t const quorum_7 = 7;
1829 std::vector<signer> const signers_ = [] {
1830 constexpr int numSigners = 4;
1831 std::uint32_t weights[] = {1, 2, 4, 4};
1832
1833 std::vector<signer> result;
1834 result.reserve(numSigners);
1835 for (int i = 0; i < numSigners; ++i)
1836 {
1837 using namespace std::literals;
1838 auto const a = Account("signer_"s + std::to_string(i));
1839 result.emplace_back(a, weights[i]);
1840 }
1841 return result;
1842 }();
1843
1844 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1845
1847 .tx(jtx::signers(Account::master, quorum_7, signers_))
1848 .close()
1850 .close();
1851
1852 std::uint32_t const claimID = 1;
1853 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1854
1855 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1856 auto const amt = XRP(1000);
1857
1858 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1859
1860 BalanceTransfer transfer(
1861 scEnv,
1863 scBob,
1864 scAlice,
1865 &payees[1],
1866 2,
1867 withClaim);
1868
1869 scEnv
1871 scAttester,
1872 jvb,
1873 mcAlice,
1874 amt,
1875 payees,
1876 true,
1877 claimID,
1878 dst,
1879 signers_,
1880 2,
1881 1))
1882 .close();
1883
1884 if (withClaim)
1885 {
1886 BEAST_EXPECT(transfer.has_not_happened());
1887
1888 // need to submit a claim transactions
1889 scEnv
1890 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1892 .close();
1893 }
1894
1895 BEAST_EXPECT(
1896 !!scEnv.claimID(jvb, claimID)); // claim id still present
1897 BEAST_EXPECT(transfer.has_not_happened());
1898 }
1899
1900 // Confirm that account create transactions happen in the correct
1901 // order. If they reach quorum out of order they should not execute
1902 // until all the previous created transactions have occurred.
1903 // Re-adding an attestation should move funds.
1904 {
1905 XEnv mcEnv(*this);
1906 XEnv scEnv(*this, true);
1907 auto const amt = XRP(1000);
1908 auto const amt_plus_reward = amt + reward;
1909
1910 {
1911 Balance door(mcEnv, mcDoor);
1912 Balance carol(mcEnv, mcCarol);
1913
1914 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1915 .close()
1917 mcAlice, jvb, scuAlice, amt, reward))
1919 mcBob, jvb, scuBob, amt, reward))
1921 mcCarol, jvb, scuCarol, amt, reward))
1922 .close();
1923
1924 BEAST_EXPECT(
1925 door.diff() ==
1926 (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
1927 tx_fee));
1928 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1929 }
1930
1933 .close();
1934
1935 {
1936 // send first batch of account create attest for all 3
1937 // account create
1938 Balance attester(scEnv, scAttester);
1939 Balance door(scEnv, Account::master);
1940
1941 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2))
1943 .multiTx(att_create_acct_vec(2, amt, scuBob, 2))
1944 .close();
1945
1946 BEAST_EXPECT(door.diff() == STAmount(0));
1947 // att_create_acct_vec return vectors of size 2, so 2*3 txns
1948 BEAST_EXPECT(attester.diff() == -multiTtxFee(6));
1949
1950 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // ca claim id present
1951 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1952 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1953 BEAST_EXPECT(
1954 scEnv.claimCount(jvb) == 0); // claim count still 0
1955 }
1956
1957 {
1958 // complete attestations for 2nd account create => should
1959 // not complete
1960 Balance attester(scEnv, scAttester);
1961 Balance door(scEnv, Account::master);
1962
1963 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 3, 2))
1964 .close();
1965
1966 BEAST_EXPECT(door.diff() == STAmount(0));
1967 // att_create_acct_vec return vectors of size 3, so 3 txns
1968 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1969
1970 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1971 BEAST_EXPECT(
1972 scEnv.claimCount(jvb) == 0); // claim count still 0
1973 }
1974
1975 {
1976 // complete attestations for 3rd account create => should
1977 // not complete
1978 Balance attester(scEnv, scAttester);
1979 Balance door(scEnv, Account::master);
1980
1981 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
1982 .close();
1983
1984 BEAST_EXPECT(door.diff() == STAmount(0));
1985 // att_create_acct_vec return vectors of size 3, so 3 txns
1986 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1987
1988 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1989 BEAST_EXPECT(
1990 scEnv.claimCount(jvb) == 0); // claim count still 0
1991 }
1992
1993 {
1994 // complete attestations for 1st account create => account
1995 // should be created
1996 Balance attester(scEnv, scAttester);
1997 Balance door(scEnv, Account::master);
1998
1999 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 3, 1))
2000 .close();
2001
2002 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2003 // att_create_acct_vec return vectors of size 3, so 3 txns
2004 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
2005 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2006
2007 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id 1 deleted
2008 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
2009 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2010 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count now 1
2011 }
2012
2013 {
2014 // resend attestations for 3rd account create => still
2015 // should not complete
2016 Balance attester(scEnv, scAttester);
2017 Balance door(scEnv, Account::master);
2018
2019 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
2020 .close();
2021
2022 BEAST_EXPECT(door.diff() == STAmount(0));
2023 // att_create_acct_vec return vectors of size 3, so 3 txns
2024 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
2025
2026 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
2027 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2028 BEAST_EXPECT(
2029 scEnv.claimCount(jvb) == 1); // claim count still 1
2030 }
2031
2032 {
2033 // resend attestations for 2nd account create => account
2034 // should be created
2035 Balance attester(scEnv, scAttester);
2036 Balance door(scEnv, Account::master);
2037
2038 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1)).close();
2039
2040 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2041 BEAST_EXPECT(attester.diff() == -tx_fee);
2042 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2043
2044 BEAST_EXPECT(!scEnv.caClaimID(jvb, 2)); // claim id 2 deleted
2045 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2046 BEAST_EXPECT(scEnv.claimCount(jvb) == 2); // claim count now 2
2047 }
2048 {
2049 // resend attestations for 3rc account create => account
2050 // should be created
2051 Balance attester(scEnv, scAttester);
2052 Balance door(scEnv, Account::master);
2053
2054 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1)).close();
2055
2056 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2057 BEAST_EXPECT(attester.diff() == -tx_fee);
2058 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2059
2060 BEAST_EXPECT(!scEnv.caClaimID(jvb, 3)); // claim id 3 deleted
2061 BEAST_EXPECT(scEnv.claimCount(jvb) == 3); // claim count now 3
2062 }
2063 }
2064
2065 // Check that creating an account with less than the minimum reserve
2066 // fails.
2067 {
2068 XEnv mcEnv(*this);
2069 XEnv scEnv(*this, true);
2070
2071 auto const amt = res0 - XRP(1);
2072 auto const amt_plus_reward = amt + reward;
2073
2074 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2075
2076 {
2077 Balance door(mcEnv, mcDoor);
2078 Balance carol(mcEnv, mcCarol);
2079
2080 mcEnv
2082 mcCarol, jvb, scuAlice, amt, reward))
2083 .close();
2084
2085 BEAST_EXPECT(door.diff() == amt_plus_reward);
2086 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2087 }
2088
2091 .close();
2092
2093 Balance attester(scEnv, scAttester);
2094 Balance door(scEnv, Account::master);
2095
2096 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)).close();
2097 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2098 BEAST_EXPECT(
2099 scEnv.claimCount(jvb) == 0); // claim count is one less
2100
2101 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2, 2)).close();
2102 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2103 BEAST_EXPECT(
2104 scEnv.claimCount(jvb) == 1); // claim count was incremented
2105
2106 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2107 BEAST_EXPECT(door.diff() == -reward);
2108 BEAST_EXPECT(!scEnv.account(scuAlice));
2109 }
2110
2111 // Check that sending funds with an account create txn to an
2112 // existing account works.
2113 {
2114 XEnv mcEnv(*this);
2115 XEnv scEnv(*this, true);
2116
2117 auto const amt = XRP(111);
2118 auto const amt_plus_reward = amt + reward;
2119
2120 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2121
2122 {
2123 Balance door(mcEnv, mcDoor);
2124 Balance carol(mcEnv, mcCarol);
2125
2126 mcEnv
2128 mcCarol, jvb, scAlice, amt, reward))
2129 .close();
2130
2131 BEAST_EXPECT(door.diff() == amt_plus_reward);
2132 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2133 }
2134
2137 .close();
2138
2139 Balance attester(scEnv, scAttester);
2140 Balance door(scEnv, Account::master);
2141 Balance alice(scEnv, scAlice);
2142
2143 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2144 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2145 BEAST_EXPECT(
2146 scEnv.claimCount(jvb) == 0); // claim count is one less
2147
2148 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2149 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2150 BEAST_EXPECT(
2151 scEnv.claimCount(jvb) == 1); // claim count was incremented
2152
2153 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2154 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2155 BEAST_EXPECT(alice.diff() == amt);
2156 }
2157
2158 // Check that sending funds to an existing account with deposit auth
2159 // set fails for account create transactions.
2160 {
2161 XEnv mcEnv(*this);
2162 XEnv scEnv(*this, true);
2163
2164 auto const amt = XRP(1000);
2165 auto const amt_plus_reward = amt + reward;
2166
2167 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2168
2169 {
2170 Balance door(mcEnv, mcDoor);
2171 Balance carol(mcEnv, mcCarol);
2172
2173 mcEnv
2175 mcCarol, jvb, scAlice, amt, reward))
2176 .close();
2177
2178 BEAST_EXPECT(door.diff() == amt_plus_reward);
2179 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2180 }
2181
2184 .tx(fset("scAlice", asfDepositAuth)) // set deposit auth
2185 .close();
2186
2187 Balance attester(scEnv, scAttester);
2188 Balance door(scEnv, Account::master);
2189 Balance alice(scEnv, scAlice);
2190
2191 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2192 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2193 BEAST_EXPECT(
2194 scEnv.claimCount(jvb) == 0); // claim count is one less
2195
2196 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2197 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2198 BEAST_EXPECT(
2199 scEnv.claimCount(jvb) == 1); // claim count was incremented
2200
2201 BEAST_EXPECT(door.diff() == -reward);
2202 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2203 BEAST_EXPECT(alice.diff() == STAmount(0));
2204 }
2205
2206 // If an account is unable to pay the reserve, check that it fails.
2207 // [greg todo] I don't know what this should test??
2208
2209 // If an attestation already exists for that server and claim id,
2210 // the new attestation should replace the old attestation
2211 {
2212 XEnv mcEnv(*this);
2213 XEnv scEnv(*this, true);
2214 auto const amt = XRP(1000);
2215 auto const amt_plus_reward = amt + reward;
2216
2217 {
2218 Balance door(mcEnv, mcDoor);
2219 Balance carol(mcEnv, mcCarol);
2220
2221 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
2222 .close()
2224 mcAlice, jvb, scuAlice, amt, reward))
2225 .close() // make sure Alice gets claim #1
2227 mcBob, jvb, scuBob, amt, reward))
2228 .close() // make sure Bob gets claim #2
2230 mcCarol, jvb, scuCarol, amt, reward))
2231 .close(); // and Carol will get claim #3
2232
2233 BEAST_EXPECT(
2234 door.diff() ==
2235 (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
2236 tx_fee));
2237 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
2238 }
2239
2240 std::uint32_t const red_quorum = 2;
2242 .tx(jtx::signers(Account::master, red_quorum, signers))
2243 .close();
2244
2245 {
2246 Balance attester(scEnv, scAttester);
2247 Balance door(scEnv, Account::master);
2248 auto const bad_amt = XRP(10);
2249 std::uint32_t txCount = 0;
2250
2251 // send attestations with incorrect amounts to for all 3
2252 // AccountCreate. They will be replaced later
2253 scEnv.multiTx(att_create_acct_vec(1, bad_amt, scuAlice, 1))
2254 .multiTx(att_create_acct_vec(2, bad_amt, scuBob, 1, 2))
2255 .multiTx(att_create_acct_vec(3, bad_amt, scuCarol, 1, 1))
2256 .close();
2257 txCount += 3;
2258
2259 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 created");
2260 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 created");
2261 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 created");
2262
2263 // note: if we send inconsistent attestations in the same
2264 // batch, the transaction errors.
2265
2266 // from now on we send correct attestations
2267 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 0))
2268 .multiTx(att_create_acct_vec(2, amt, scuBob, 1, 2))
2269 .multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 4))
2270 .close();
2271 txCount += 3;
2272
2273 BEAST_EXPECTS(
2274 !!scEnv.caClaimID(jvb, 1), "claim id 1 still there");
2275 BEAST_EXPECTS(
2276 !!scEnv.caClaimID(jvb, 2), "claim id 2 still there");
2277 BEAST_EXPECTS(
2278 !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2279 BEAST_EXPECTS(
2280 scEnv.claimCount(jvb) == 0, "No account created yet");
2281
2282 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 1))
2283 .close();
2284 txCount += 1;
2285
2286 BEAST_EXPECTS(
2287 !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2288 BEAST_EXPECTS(
2289 scEnv.claimCount(jvb) == 0, "No account created yet");
2290
2291 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 2))
2292 .close();
2293 txCount += 1;
2294
2295 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 deleted");
2296 BEAST_EXPECTS(scEnv.claimCount(jvb) == 1, "scuAlice created");
2297
2298 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1, 3))
2299 .multiTx(
2300 att_create_acct_vec(1, amt, scuAlice, 1, 3),
2302 .close();
2303 txCount += 2;
2304
2305 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 2), "claim id 2 deleted");
2306 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 not added");
2307 BEAST_EXPECTS(
2308 scEnv.claimCount(jvb) == 2, "scuAlice & scuBob created");
2309
2310 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 0))
2311 .close();
2312 txCount += 1;
2313
2314 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 3), "claim id 3 deleted");
2315 BEAST_EXPECTS(
2316 scEnv.claimCount(jvb) == 3, "All 3 accounts created");
2317
2318 // because of the division of the rewards among attesters,
2319 // sometimes a couple drops are left over unspent in the
2320 // door account (here 2 drops)
2321 BEAST_EXPECT(
2322 multiply(amt_plus_reward, STAmount(3), xrpIssue()) +
2323 door.diff() <
2324 drops(3));
2325 BEAST_EXPECT(attester.diff() == -multiTtxFee(txCount));
2326 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2327 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2328 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2329 }
2330 }
2331
2332 // If attestation moves funds, confirm the claim ledger objects are
2333 // removed (for both account create and "regular" transactions)
2334 // [greg] we do this in all attestation tests
2335
2336 // coverage test: add_attestation transaction with incorrect flag
2337 {
2338 XEnv scEnv(*this, true);
2341 .close()
2343 scAttester,
2344 jvb,
2345 mcAlice,
2346 XRP(1000),
2347 payees[0],
2348 true,
2349 1,
2350 {},
2351 signers[0]),
2354 .close();
2355 }
2356
2357 // coverage test: add_attestation with xchain feature
2358 // disabled
2359 {
2360 XEnv scEnv(*this, true);
2363 .disableFeature(featureXChainBridge)
2364 .close()
2366 scAttester,
2367 jvb,
2368 mcAlice,
2369 XRP(1000),
2370 payees[0],
2371 true,
2372 1,
2373 {},
2374 signers[0]),
2376 .close();
2377 }
2378 }
2379
2380 void
2382 {
2383 using namespace jtx;
2384
2385 testcase("Add Non Batch Claim Attestation");
2386
2387 {
2388 XEnv mcEnv(*this);
2389 XEnv scEnv(*this, true);
2390 std::uint32_t const claimID = 1;
2391
2392 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2393
2396 .close()
2398 .close();
2399
2400 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
2401
2402 Account const dst{scBob};
2403 auto const amt = XRP(1000);
2404 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2405
2406 auto const dstStartBalance = scEnv.env_.balance(dst);
2407
2408 for (int i = 0; i < signers.size(); ++i)
2409 {
2410 auto const att = claim_attestation(
2411 scAttester,
2412 jvb,
2413 mcAlice,
2414 amt,
2415 payees[i],
2416 true,
2417 claimID,
2418 dst,
2419 signers[i]);
2420
2421 TER const expectedTER =
2423 if (i + 1 == quorum)
2424 scEnv.tx(att, ter(expectedTER)).close();
2425 else
2426 scEnv.tx(att, ter(expectedTER)).close();
2427
2428 if (i + 1 < quorum)
2429 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2430 else
2431 BEAST_EXPECT(
2432 dstStartBalance + amt == scEnv.env_.balance(dst));
2433 }
2434 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2435 }
2436
2437 {
2454 XEnv mcEnv(*this);
2455 XEnv scEnv(*this, true);
2456 auto const amt = XRP(1000);
2457 std::uint32_t const claimID = 1;
2458
2459 for (auto i = 0; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2; ++i)
2460 scEnv.fund(amt, alt_signers[i].account);
2461
2462 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2463
2466 .close()
2468 .close();
2469
2470 Account const dst{scBob};
2471 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2472 auto const dstStartBalance = scEnv.env_.balance(dst);
2473
2474 {
2475 // G1: master key
2476 auto att = claim_attestation(
2477 scAttester,
2478 jvb,
2479 mcAlice,
2480 amt,
2481 payees[0],
2482 true,
2483 claimID,
2484 dst,
2485 alt_signers[0]);
2486 scEnv.tx(att).close();
2487 }
2488 {
2489 // G2: regular key
2490 // alt_signers[0] is the regular key of alt_signers[1]
2491 // There should be 2 attestations after the transaction
2492 scEnv
2493 .tx(jtx::regkey(
2494 alt_signers[1].account, alt_signers[0].account))
2495 .close();
2496 auto att = claim_attestation(
2497 scAttester,
2498 jvb,
2499 mcAlice,
2500 amt,
2501 payees[1],
2502 true,
2503 claimID,
2504 dst,
2505 alt_signers[0]);
2506 att[sfAttestationSignerAccount.getJsonName()] =
2507 alt_signers[1].account.human();
2508 scEnv.tx(att).close();
2509 }
2510 {
2511 // B3: public key and non-exist (unfunded) account mismatch
2512 // G3: public key and non-exist (unfunded) account match
2513 auto const unfundedSigner1 =
2515 auto const unfundedSigner2 =
2517 auto att = claim_attestation(
2518 scAttester,
2519 jvb,
2520 mcAlice,
2521 amt,
2523 true,
2524 claimID,
2525 dst,
2526 unfundedSigner1);
2527 att[sfAttestationSignerAccount.getJsonName()] =
2528 unfundedSigner2.account.human();
2530 .close();
2531 att[sfAttestationSignerAccount.getJsonName()] =
2532 unfundedSigner1.account.human();
2533 scEnv.tx(att).close();
2534 }
2535 {
2536 // B2: single item signer list
2537 std::vector<signer> tempSignerList = {signers[0]};
2538 scEnv.tx(
2539 jtx::signers(alt_signers[2].account, 1, tempSignerList));
2540 auto att = claim_attestation(
2541 scAttester,
2542 jvb,
2543 mcAlice,
2544 amt,
2545 payees[2],
2546 true,
2547 claimID,
2548 dst,
2549 tempSignerList.front());
2550 att[sfAttestationSignerAccount.getJsonName()] =
2551 alt_signers[2].account.human();
2553 .close();
2554 }
2555 {
2556 // B1: disabled master key
2557 scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0))
2558 .close();
2559 auto att = claim_attestation(
2560 scAttester,
2561 jvb,
2562 mcAlice,
2563 amt,
2564 payees[2],
2565 true,
2566 claimID,
2567 dst,
2568 alt_signers[2]);
2570 .close();
2571 }
2572 {
2573 // --B4: not on signer list
2574 auto att = claim_attestation(
2575 scAttester,
2576 jvb,
2577 mcAlice,
2578 amt,
2579 payees[0],
2580 true,
2581 claimID,
2582 dst,
2583 signers[0]);
2584 scEnv.tx(att, ter(tecNO_PERMISSION)).close();
2585 }
2586 {
2587 // --B5: missing sfAttestationSignerAccount field
2588 // Then submit the one with the field. Should rearch quorum.
2589 auto att = claim_attestation(
2590 scAttester,
2591 jvb,
2592 mcAlice,
2593 amt,
2594 payees[3],
2595 true,
2596 claimID,
2597 dst,
2598 alt_signers[3]);
2599 att.removeMember(sfAttestationSignerAccount.getJsonName());
2600 scEnv.tx(att, ter(temMALFORMED)).close();
2601 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2602 att[sfAttestationSignerAccount.getJsonName()] =
2603 alt_signers[3].account.human();
2604 scEnv.tx(att).close();
2605 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2606 }
2607 }
2608 }
2609
2610 void
2612 {
2613 using namespace jtx;
2614
2615 testcase("Add Non Batch Account Create Attestation");
2616
2617 XEnv mcEnv(*this);
2618 XEnv scEnv(*this, true);
2619
2620 XRPAmount tx_fee = mcEnv.txFee();
2621
2622 Account a{"a"};
2623 Account doorA{"doorA"};
2624
2625 STAmount funds{XRP(10000)};
2626 mcEnv.fund(funds, a);
2627 mcEnv.fund(funds, doorA);
2628
2629 Account ua{"ua"}; // unfunded account we want to create
2630
2631 BridgeDef xrp_b{
2632 doorA,
2633 xrpIssue(),
2635 xrpIssue(),
2636 XRP(1), // reward
2637 XRP(20), // minAccountCreate
2638 4, // quorum
2639 signers,
2641
2642 xrp_b.initBridge(mcEnv, scEnv);
2643
2644 auto const amt = XRP(777);
2645 auto const amt_plus_reward = amt + xrp_b.reward;
2646 {
2647 Balance bal_doorA(mcEnv, doorA);
2648 Balance bal_a(mcEnv, a);
2649
2650 mcEnv
2652 a, xrp_b.jvb, ua, amt, xrp_b.reward))
2653 .close();
2654
2655 BEAST_EXPECT(bal_doorA.diff() == amt_plus_reward);
2656 BEAST_EXPECT(bal_a.diff() == -(amt_plus_reward + tx_fee));
2657 }
2658
2659 for (int i = 0; i < signers.size(); ++i)
2660 {
2661 auto const att = create_account_attestation(
2662 signers[0].account,
2663 xrp_b.jvb,
2664 a,
2665 amt,
2666 xrp_b.reward,
2667 signers[i].account,
2668 true,
2669 1,
2670 ua,
2671 signers[i]);
2672 TER const expectedTER = i < xrp_b.quorum
2673 ? tesSUCCESS
2675
2676 scEnv.tx(att, ter(expectedTER)).close();
2677 if (i + 1 < xrp_b.quorum)
2678 BEAST_EXPECT(!scEnv.env_.le(ua));
2679 else
2680 BEAST_EXPECT(scEnv.env_.le(ua));
2681 }
2682 BEAST_EXPECT(scEnv.env_.le(ua));
2683 }
2684
2685 void
2687 {
2688 using namespace jtx;
2689
2690 XRPAmount res0 = reserve(0);
2691 XRPAmount tx_fee = txFee();
2692
2693 testcase("Claim");
2694
2695 // Claim where the amount matches what is attested to, to an account
2696 // that exists, and there are enough attestations to reach a quorum
2697 // => should succeed
2698 // -----------------------------------------------------------------
2699 for (auto withClaim : {false, true})
2700 {
2701 XEnv mcEnv(*this);
2702 XEnv scEnv(*this, true);
2703
2704 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2705
2708 .close()
2710 .close();
2711
2712 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2713 auto const amt = XRP(1000);
2714 std::uint32_t const claimID = 1;
2715 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2716
2717 BalanceTransfer transfer(
2718 scEnv,
2720 scBob,
2721 scAlice,
2722 &payees[0],
2724 withClaim);
2725
2726 scEnv
2728 scAttester,
2729 jvb,
2730 mcAlice,
2731 amt,
2732 payees,
2733 true,
2734 claimID,
2735 dst,
2736 signers))
2737 .close();
2738 if (withClaim)
2739 {
2740 BEAST_EXPECT(transfer.has_not_happened());
2741
2742 // need to submit a claim transactions
2743 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
2744 .close();
2745 }
2746
2747 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2748 }
2749
2750 // Claim with just one attestation signed by the Master key
2751 // => should not succeed
2752 // -----------------------------------------------------------------
2753 for (auto withClaim : {false, true})
2754 {
2755 XEnv mcEnv(*this);
2756 XEnv scEnv(*this, true);
2757
2758 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2759
2760 scEnv
2762 //.tx(jtx::signers(Account::master, quorum, signers))
2763 .close()
2765 .close();
2766
2767 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2768 auto const amt = XRP(1000);
2769 std::uint32_t const claimID = 1;
2770 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2771
2772 BalanceTransfer transfer(
2773 scEnv,
2775 scBob,
2776 scAlice,
2777 &payees[0],
2778 1,
2779 withClaim);
2780
2781 jtx::signer master_signer(Account::master);
2782 scEnv
2784 scAttester,
2785 jvb,
2786 mcAlice,
2787 amt,
2788 payees[0],
2789 true,
2790 claimID,
2791 dst,
2792 master_signer),
2794 .close();
2795
2796 BEAST_EXPECT(transfer.has_not_happened());
2797 }
2798
2799 // Claim with just one attestation signed by a regular key
2800 // associated to the master account
2801 // => should not succeed
2802 // -----------------------------------------------------------------
2803 for (auto withClaim : {false, true})
2804 {
2805 XEnv mcEnv(*this);
2806 XEnv scEnv(*this, true);
2807
2808 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2809
2810 scEnv
2812 //.tx(jtx::signers(Account::master, quorum, signers))
2814 .close()
2816 .close();
2817
2818 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2819 auto const amt = XRP(1000);
2820 std::uint32_t const claimID = 1;
2821 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2822
2823 BalanceTransfer transfer(
2824 scEnv,
2826 scBob,
2827 scAlice,
2828 &payees[0],
2829 1,
2830 withClaim);
2831
2832 jtx::signer master_signer(payees[0]);
2833 scEnv
2835 scAttester,
2836 jvb,
2837 mcAlice,
2838 amt,
2839 payees[0],
2840 true,
2841 claimID,
2842 dst,
2843 master_signer),
2845 .close();
2846
2847 BEAST_EXPECT(transfer.has_not_happened());
2848 }
2849
2850 // Claim against non-existent bridge
2851 // ---------------------------------
2852 for (auto withClaim : {false, true})
2853 {
2854 XEnv mcEnv(*this);
2855 XEnv scEnv(*this, true);
2856
2857 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2858
2859 auto jvb_unknown =
2861
2864 .close()
2866 scAlice, jvb_unknown, reward, mcAlice),
2868 .close();
2869
2870 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2871 auto const amt = XRP(1000);
2872 std::uint32_t const claimID = 1;
2873 mcEnv
2874 .tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst),
2876 .close();
2877
2878 BalanceTransfer transfer(
2879 scEnv, Account::master, scBob, scAlice, payees, withClaim);
2880 scEnv
2882 scAttester,
2883 jvb_unknown,
2884 mcAlice,
2885 amt,
2886 payees[0],
2887 true,
2888 claimID,
2889 dst,
2890 signers[0]),
2892 .close();
2893
2894 if (withClaim)
2895 {
2896 BEAST_EXPECT(transfer.has_not_happened());
2897
2898 // need to submit a claim transactions
2899 scEnv
2900 .tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob),
2902 .close();
2903 }
2904
2905 BEAST_EXPECT(transfer.has_not_happened());
2906 }
2907
2908 // Claim against non-existent claim id
2909 // -----------------------------------
2910 for (auto withClaim : {false, true})
2911 {
2912 XEnv mcEnv(*this);
2913 XEnv scEnv(*this, true);
2914
2915 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2916
2919 .close()
2921 .close();
2922
2923 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2924 auto const amt = XRP(1000);
2925 std::uint32_t const claimID = 1;
2926 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2927
2928 BalanceTransfer transfer(
2929 scEnv, Account::master, scBob, scAlice, payees, withClaim);
2930
2931 // attest using non-existent claim id
2932 scEnv
2934 scAttester,
2935 jvb,
2936 mcAlice,
2937 amt,
2938 payees[0],
2939 true,
2940 999,
2941 dst,
2942 signers[0]),
2944 .close();
2945 if (withClaim)
2946 {
2947 BEAST_EXPECT(transfer.has_not_happened());
2948
2949 // claim using non-existent claim id
2950 scEnv
2951 .tx(xchain_claim(scAlice, jvb, 999, amt, scBob),
2953 .close();
2954 }
2955
2956 BEAST_EXPECT(transfer.has_not_happened());
2957 }
2958
2959 // Claim against a claim id owned by another account
2960 // -------------------------------------------------
2961 for (auto withClaim : {false, true})
2962 {
2963 XEnv mcEnv(*this);
2964 XEnv scEnv(*this, true);
2965
2966 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2967
2970 .close()
2972 .close();
2973
2974 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2975 auto const amt = XRP(1000);
2976 std::uint32_t const claimID = 1;
2977 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2978
2979 BalanceTransfer transfer(
2980 scEnv,
2982 scBob,
2983 scAlice,
2984 &payees[0],
2986 withClaim);
2987
2988 scEnv
2990 scAttester,
2991 jvb,
2992 mcAlice,
2993 amt,
2994 payees,
2995 true,
2996 claimID,
2997 dst,
2998 signers))
2999 .close();
3000 if (withClaim)
3001 {
3002 BEAST_EXPECT(transfer.has_not_happened());
3003
3004 // submit a claim transaction with the wrong account (scGw
3005 // instead of scAlice)
3006 scEnv
3007 .tx(xchain_claim(scGw, jvb, claimID, amt, scBob),
3009 .close();
3010 BEAST_EXPECT(transfer.has_not_happened());
3011 }
3012 else
3013 {
3014 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3015 }
3016 }
3017
3018 // Claim against a claim id with no attestations
3019 // ---------------------------------------------
3020 for (auto withClaim : {false, true})
3021 {
3022 XEnv mcEnv(*this);
3023 XEnv scEnv(*this, true);
3024
3025 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3026
3029 .close()
3031 .close();
3032
3033 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3034 auto const amt = XRP(1000);
3035 std::uint32_t const claimID = 1;
3036 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3037
3038 BalanceTransfer transfer(
3039 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3040
3041 // don't send any attestations
3042
3043 if (withClaim)
3044 {
3045 BEAST_EXPECT(transfer.has_not_happened());
3046
3047 // need to submit a claim transactions
3048 scEnv
3049 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3051 .close();
3052 }
3053
3054 BEAST_EXPECT(transfer.has_not_happened());
3055 }
3056
3057 // Claim against a claim id with attestations, but not enough to
3058 // make a quorum
3059 // --------------------------------------------------------------------
3060 for (auto withClaim : {false, true})
3061 {
3062 XEnv mcEnv(*this);
3063 XEnv scEnv(*this, true);
3064
3065 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3066
3069 .close()
3071 .close();
3072
3073 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3074 auto const amt = XRP(1000);
3075 std::uint32_t const claimID = 1;
3076 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3077
3078 BalanceTransfer transfer(
3079 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3080
3081 auto tooFew = quorum - 1;
3082 scEnv
3084 scAttester,
3085 jvb,
3086 mcAlice,
3087 amt,
3088 payees,
3089 true,
3090 claimID,
3091 dst,
3092 signers,
3093 tooFew))
3094 .close();
3095 if (withClaim)
3096 {
3097 BEAST_EXPECT(transfer.has_not_happened());
3098
3099 // need to submit a claim transactions
3100 scEnv
3101 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3103 .close();
3104 }
3105
3106 BEAST_EXPECT(transfer.has_not_happened());
3107 }
3108
3109 // Claim id of zero
3110 // ----------------
3111 for (auto withClaim : {false, true})
3112 {
3113 XEnv mcEnv(*this);
3114 XEnv scEnv(*this, true);
3115
3116 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3117
3120 .close()
3122 .close();
3123
3124 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3125 auto const amt = XRP(1000);
3126 std::uint32_t const claimID = 1;
3127 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3128
3129 BalanceTransfer transfer(
3130 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3131
3132 scEnv
3133 .multiTx(
3135 scAttester,
3136 jvb,
3137 mcAlice,
3138 amt,
3139 payees,
3140 true,
3141 0,
3142 dst,
3143 signers),
3145 .close();
3146 if (withClaim)
3147 {
3148 BEAST_EXPECT(transfer.has_not_happened());
3149
3150 // need to submit a claim transactions
3151 scEnv
3152 .tx(xchain_claim(scAlice, jvb, 0, amt, scBob),
3154 .close();
3155 }
3156
3157 BEAST_EXPECT(transfer.has_not_happened());
3158 }
3159
3160 // Claim issue that does not match the expected issue on the bridge
3161 // (either LockingChainIssue or IssuingChainIssue, depending on the
3162 // chain). The claim id should already have enough attestations to
3163 // reach a quorum for this amount (for a different issuer).
3164 // ---------------------------------------------------------------------
3165 for (auto withClaim : {true})
3166 {
3167 XEnv mcEnv(*this);
3168 XEnv scEnv(*this, true);
3169
3170 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3171
3174 .close()
3176 .close();
3177
3178 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3179 auto const amt = XRP(1000);
3180 std::uint32_t const claimID = 1;
3181 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3182
3183 BalanceTransfer transfer(
3184 scEnv,
3186 scBob,
3187 scAlice,
3188 &payees[0],
3190 withClaim);
3191
3192 scEnv
3194 scAttester,
3195 jvb,
3196 mcAlice,
3197 amt,
3198 payees,
3199 true,
3200 claimID,
3201 dst,
3202 signers))
3203 .close();
3204
3205 if (withClaim)
3206 {
3207 BEAST_EXPECT(transfer.has_not_happened());
3208
3209 // need to submit a claim transactions
3210 scEnv
3211 .tx(xchain_claim(scAlice, jvb, claimID, scUSD(1000), scBob),
3213 .close();
3214 }
3215
3216 BEAST_EXPECT(transfer.has_not_happened());
3217 }
3218
3219 // Claim to a destination that does not already exist on the chain
3220 // -----------------------------------------------------------------
3221 for (auto withClaim : {true})
3222 {
3223 XEnv mcEnv(*this);
3224 XEnv scEnv(*this, true);
3225
3226 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3227
3230 .close()
3232 .close();
3233
3234 auto dst(withClaim ? std::nullopt : std::optional<Account>{scuBob});
3235 auto const amt = XRP(1000);
3236 std::uint32_t const claimID = 1;
3237 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3238
3239 BalanceTransfer transfer(
3240 scEnv,
3242 scBob,
3243 scAlice,
3244 &payees[0],
3246 withClaim);
3247
3248 scEnv
3250 scAttester,
3251 jvb,
3252 mcAlice,
3253 amt,
3254 payees,
3255 true,
3256 claimID,
3257 dst,
3258 signers))
3259 .close();
3260 if (withClaim)
3261 {
3262 BEAST_EXPECT(transfer.has_not_happened());
3263
3264 // need to submit a claim transactions
3265 scEnv
3266 .tx(xchain_claim(scAlice, jvb, claimID, amt, scuBob),
3267 ter(tecNO_DST))
3268 .close();
3269 }
3270
3271 BEAST_EXPECT(transfer.has_not_happened());
3272 }
3273
3274 // Claim where the claim id owner does not have enough XRP to pay
3275 // the reward
3276 // ------------------------------------------------------------------
3277 for (auto withClaim : {false, true})
3278 {
3279 XEnv mcEnv(*this);
3280 XEnv scEnv(*this, true);
3281
3282 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3283 STAmount huge_reward{XRP(20000)};
3284 BEAST_EXPECT(huge_reward > scEnv.balance(scAlice));
3285
3286 scEnv.tx(create_bridge(Account::master, jvb, huge_reward))
3288 .close()
3289 .tx(xchain_create_claim_id(scAlice, jvb, huge_reward, mcAlice))
3290 .close();
3291
3292 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3293 auto const amt = XRP(1000);
3294 std::uint32_t const claimID = 1;
3295 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3296
3297 BalanceTransfer transfer(
3298 scEnv,
3300 scBob,
3301 scAlice,
3302 &payees[0],
3304 withClaim);
3305
3306 if (withClaim)
3307 {
3308 scEnv
3310 scAttester,
3311 jvb,
3312 mcAlice,
3313 amt,
3314 payees,
3315 true,
3316 claimID,
3317 dst,
3318 signers))
3319 .close();
3320 BEAST_EXPECT(transfer.has_not_happened());
3321
3322 // need to submit a claim transactions
3323 scEnv
3324 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3326 .close();
3327 }
3328 else
3329 {
3330 auto txns = claim_attestations(
3331 scAttester,
3332 jvb,
3333 mcAlice,
3334 amt,
3335 payees,
3336 true,
3337 claimID,
3338 dst,
3339 signers);
3340 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3341 {
3342 scEnv.tx(txns[i]).close();
3343 }
3344 scEnv.tx(txns.back());
3345 scEnv.close();
3346 // The attestation should succeed, because it adds an
3347 // attestation, but the claim should fail with insufficient
3348 // funds
3349 scEnv
3350 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3352 .close();
3353 }
3354
3355 BEAST_EXPECT(transfer.has_not_happened());
3356 }
3357
3358 // Claim where the claim id owner has enough XRP to pay the reward,
3359 // but it would put his balance below the reserve
3360 // --------------------------------------------------------------------
3361 for (auto withClaim : {false, true})
3362 {
3363 XEnv mcEnv(*this);
3364 XEnv scEnv(*this, true);
3365
3366 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3367
3370 .fund(
3371 res0 + reward,
3372 scuAlice) // just not enough because of fees
3373 .close()
3376 .close();
3377
3378 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3379 auto const amt = XRP(1000);
3380 std::uint32_t const claimID = 1;
3381 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3382
3383 BalanceTransfer transfer(
3384 scEnv, Account::master, scBob, scuAlice, payees, withClaim);
3385
3386 scEnv
3388 scAttester,
3389 jvb,
3390 mcAlice,
3391 amt,
3392 payees[0],
3393 true,
3394 claimID,
3395 dst,
3396 signers[0]),
3398 .close();
3399 if (withClaim)
3400 {
3401 BEAST_EXPECT(transfer.has_not_happened());
3402
3403 // need to submit a claim transactions
3404 scEnv
3405 .tx(xchain_claim(scuAlice, jvb, claimID, amt, scBob),
3407 .close();
3408 }
3409
3410 BEAST_EXPECT(transfer.has_not_happened());
3411 }
3412
3413 // Pay to an account with deposit auth set
3414 // ---------------------------------------
3415 for (auto withClaim : {false, true})
3416 {
3417 XEnv mcEnv(*this);
3418 XEnv scEnv(*this, true);
3419
3420 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3421
3424 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3425 .close()
3427 .close();
3428
3429 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3430 auto const amt = XRP(1000);
3431 std::uint32_t const claimID = 1;
3432 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3433
3434 BalanceTransfer transfer(
3435 scEnv,
3437 scBob,
3438 scAlice,
3439 &payees[0],
3441 withClaim);
3442 auto txns = claim_attestations(
3443 scAttester,
3444 jvb,
3445 mcAlice,
3446 amt,
3447 payees,
3448 true,
3449 claimID,
3450 dst,
3451 signers);
3452 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3453 {
3454 scEnv.tx(txns[i]).close();
3455 }
3456 if (withClaim)
3457 {
3458 scEnv.tx(txns.back()).close();
3459
3460 BEAST_EXPECT(transfer.has_not_happened());
3461
3462 // need to submit a claim transactions
3463 scEnv
3464 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3466 .close();
3467
3468 // the transfer failed, but check that we can still use the
3469 // claimID with a different account
3470 Balance scCarol_bal(scEnv, scCarol);
3471
3472 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3473 .close();
3474 BEAST_EXPECT(scCarol_bal.diff() == amt);
3475 }
3476 else
3477 {
3478 scEnv.tx(txns.back()).close();
3479 scEnv
3480 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3482 .close();
3483 // A way would be to remove deposit auth and resubmit the
3484 // attestations (even though the witness servers won't do
3485 // it)
3486 scEnv
3487 .tx(fset("scBob", 0, asfDepositAuth)) // clear deposit auth
3488 .close();
3489
3490 Balance scBob_bal(scEnv, scBob);
3491 scEnv.tx(txns.back()).close();
3492 BEAST_EXPECT(scBob_bal.diff() == amt);
3493 }
3494 }
3495
3496 // Pay to an account with Destination Tag set
3497 // ------------------------------------------
3498 for (auto withClaim : {false, true})
3499 {
3500 XEnv mcEnv(*this);
3501 XEnv scEnv(*this, true);
3502
3503 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3504
3507 .tx(fset("scBob", asfRequireDest)) // set dest tag
3508 .close()
3510 .close();
3511
3512 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3513 auto const amt = XRP(1000);
3514 std::uint32_t const claimID = 1;
3515 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3516
3517 BalanceTransfer transfer(
3518 scEnv,
3520 scBob,
3521 scAlice,
3522 &payees[0],
3524 withClaim);
3525 auto txns = claim_attestations(
3526 scAttester,
3527 jvb,
3528 mcAlice,
3529 amt,
3530 payees,
3531 true,
3532 claimID,
3533 dst,
3534 signers);
3535 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3536 {
3537 scEnv.tx(txns[i]).close();
3538 }
3539 if (withClaim)
3540 {
3541 scEnv.tx(txns.back()).close();
3542 BEAST_EXPECT(transfer.has_not_happened());
3543
3544 // need to submit a claim transactions
3545 scEnv
3546 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3548 .close();
3549
3550 // the transfer failed, but check that we can still use the
3551 // claimID with a different account
3552 Balance scCarol_bal(scEnv, scCarol);
3553
3554 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3555 .close();
3556 BEAST_EXPECT(scCarol_bal.diff() == amt);
3557 }
3558 else
3559 {
3560 scEnv.tx(txns.back()).close();
3561 scEnv
3562 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3564 .close();
3565 // A way would be to remove the destination tag requirement
3566 // and resubmit the attestations (even though the witness
3567 // servers won't do it)
3568 scEnv
3569 .tx(fset("scBob", 0, asfRequireDest)) // clear dest tag
3570 .close();
3571
3572 Balance scBob_bal(scEnv, scBob);
3573
3574 scEnv.tx(txns.back()).close();
3575 BEAST_EXPECT(scBob_bal.diff() == amt);
3576 }
3577 }
3578
3579 // Pay to an account with deposit auth set. Check that the attestations
3580 // are still validated and that we can used the claimID to transfer the
3581 // funds to a different account (which doesn't have deposit auth set)
3582 // --------------------------------------------------------------------
3583 {
3584 XEnv mcEnv(*this);
3585 XEnv scEnv(*this, true);
3586
3587 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3588
3591 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3592 .close()
3594 .close();
3595
3596 auto dst(std::optional<Account>{scBob});
3597 auto const amt = XRP(1000);
3598 std::uint32_t const claimID = 1;
3599 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3600
3601 // we should be able to submit the attestations, but the transfer
3602 // should not occur because dest account has deposit auth set
3603 Balance scBob_bal(scEnv, scBob);
3604
3606 scAttester,
3607 jvb,
3608 mcAlice,
3609 amt,
3610 payees,
3611 true,
3612 claimID,
3613 dst,
3614 signers));
3615 BEAST_EXPECT(scBob_bal.diff() == STAmount(0));
3616
3617 // Check that check that we still can use the claimID to transfer
3618 // the amount to a different account
3619 Balance scCarol_bal(scEnv, scCarol);
3620
3621 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
3622 BEAST_EXPECT(scCarol_bal.diff() == amt);
3623 }
3624
3625 // Claim where the amount different from what is attested to
3626 // ---------------------------------------------------------
3627 for (auto withClaim : {true})
3628 {
3629 XEnv mcEnv(*this);
3630 XEnv scEnv(*this, true);
3631
3632 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3633
3636 .close()
3638 .close();
3639
3640 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3641 auto const amt = XRP(1000);
3642 std::uint32_t const claimID = 1;
3643 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3644
3645 BalanceTransfer transfer(
3646 scEnv,
3648 scBob,
3649 scAlice,
3650 &payees[0],
3652 withClaim);
3654 scAttester,
3655 jvb,
3656 mcAlice,
3657 amt,
3658 payees,
3659 true,
3660 claimID,
3661 dst,
3662 signers));
3663 if (withClaim)
3664 {
3665 BEAST_EXPECT(transfer.has_not_happened());
3666
3667 // claim wrong amount
3668 scEnv
3669 .tx(xchain_claim(scAlice, jvb, claimID, one_xrp, scBob),
3671 .close();
3672 }
3673
3674 BEAST_EXPECT(transfer.has_not_happened());
3675 }
3676
3677 // Verify that rewards are paid from the account that owns the claim
3678 // id
3679 // --------------------------------------------------------------------
3680 for (auto withClaim : {false, true})
3681 {
3682 XEnv mcEnv(*this);
3683 XEnv scEnv(*this, true);
3684
3685 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3686
3689 .close()
3691 .close();
3692
3693 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3694 auto const amt = XRP(1000);
3695 std::uint32_t const claimID = 1;
3696 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3697
3698 BalanceTransfer transfer(
3699 scEnv,
3701 scBob,
3702 scAlice,
3703 &payees[0],
3705 withClaim);
3706 Balance scAlice_bal(scEnv, scAlice);
3708 scAttester,
3709 jvb,
3710 mcAlice,
3711 amt,
3712 payees,
3713 true,
3714 claimID,
3715 dst,
3716 signers));
3717
3718 STAmount claim_cost = reward;
3719
3720 if (withClaim)
3721 {
3722 BEAST_EXPECT(transfer.has_not_happened());
3723
3724 // need to submit a claim transactions
3725 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3726 .close();
3727 claim_cost += tx_fee;
3728 }
3729
3730 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3731 BEAST_EXPECT(
3732 scAlice_bal.diff() == -claim_cost); // because reward % 4 == 0
3733 }
3734
3735 // Verify that if a reward is not evenly divisible among the reward
3736 // accounts, the remaining amount goes to the claim id owner.
3737 // ----------------------------------------------------------------
3738 for (auto withClaim : {false, true})
3739 {
3740 XEnv mcEnv(*this);
3741 XEnv scEnv(*this, true);
3742
3744
3747 .close()
3749 .close();
3750
3751 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3752 auto const amt = XRP(1000);
3753 std::uint32_t const claimID = 1;
3754 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3755
3756 BalanceTransfer transfer(
3757 scEnv,
3759 scBob,
3760 scAlice,
3761 &payees[0],
3763 withClaim);
3764 Balance scAlice_bal(scEnv, scAlice);
3766 scAttester,
3767 jvb,
3768 mcAlice,
3769 amt,
3770 payees,
3771 true,
3772 claimID,
3773 dst,
3774 signers));
3775 STAmount claim_cost = tiny_reward;
3776
3777 if (withClaim)
3778 {
3779 BEAST_EXPECT(transfer.has_not_happened());
3780
3781 // need to submit a claim transactions
3782 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3783 .close();
3784 claim_cost += tx_fee;
3785 }
3786
3787 BEAST_EXPECT(transfer.has_happened(amt, tiny_reward_split));
3788 BEAST_EXPECT(
3789 scAlice_bal.diff() == -(claim_cost - tiny_reward_remainder));
3790 }
3791
3792 // If a reward distribution fails for one of the reward accounts
3793 // (the reward account doesn't exist or has deposit auth set), then
3794 // the txn should still succeed, but that portion should go to the
3795 // claim id owner.
3796 // -------------------------------------------------------------------
3797 for (auto withClaim : {false, true})
3798 {
3799 XEnv mcEnv(*this);
3800 XEnv scEnv(*this, true);
3801
3802 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3803
3804 std::vector<Account> alt_payees{payees.begin(), payees.end() - 1};
3805 alt_payees.back() = Account("inexistent");
3806
3809 .close()
3811 .close();
3812
3813 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3814 auto const amt = XRP(1000);
3815 std::uint32_t const claimID = 1;
3816 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3817
3818 BalanceTransfer transfer(
3819 scEnv,
3821 scBob,
3822 scAlice,
3823 &payees[0],
3825 withClaim);
3827 scAttester,
3828 jvb,
3829 mcAlice,
3830 amt,
3831 alt_payees,
3832 true,
3833 claimID,
3834 dst,
3835 signers));
3836
3837 if (withClaim)
3838 {
3839 BEAST_EXPECT(transfer.has_not_happened());
3840
3841 // need to submit a claim transactions
3842 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3843 .close();
3844 }
3845
3846 // this also checks that only 3 * split_reward was deducted from
3847 // scAlice (the payor account), since we passed alt_payees to
3848 // BalanceTransfer
3849 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3850 }
3851
3852 for (auto withClaim : {false, true})
3853 {
3854 XEnv mcEnv(*this);
3855 XEnv scEnv(*this, true);
3856
3857 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3858 auto& unpaid = payees[UT_XCHAIN_DEFAULT_QUORUM - 1];
3861 .tx(fset(unpaid, asfDepositAuth))
3862 .close()
3864 .close();
3865
3866 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3867 auto const amt = XRP(1000);
3868 std::uint32_t const claimID = 1;
3869 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3870
3871 // balance of last signer should not change (has deposit auth)
3872 Balance last_signer(scEnv, unpaid);
3873
3874 // make sure all signers except the last one get the
3875 // split_reward
3876
3877 BalanceTransfer transfer(
3878 scEnv,
3880 scBob,
3881 scAlice,
3882 &payees[0],
3884 withClaim);
3886 scAttester,
3887 jvb,
3888 mcAlice,
3889 amt,
3890 payees,
3891 true,
3892 claimID,
3893 dst,
3894 signers));
3895
3896 if (withClaim)
3897 {
3898 BEAST_EXPECT(transfer.has_not_happened());
3899
3900 // need to submit a claim transactions
3901 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3902 .close();
3903 }
3904
3905 // this also checks that only 3 * split_reward was deducted from
3906 // scAlice (the payor account), since we passed payees.size() -
3907 // 1 to BalanceTransfer
3908 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3909
3910 // and make sure the account with deposit auth received nothing
3911 BEAST_EXPECT(last_signer.diff() == STAmount(0));
3912 }
3913
3914 // coverage test: xchain_claim transaction with incorrect flag
3915 XEnv(*this, true)
3917 .close()
3918 .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3921 .close();
3922
3923 // coverage test: xchain_claim transaction with xchain feature
3924 // disabled
3925 XEnv(*this, true)
3927 .disableFeature(featureXChainBridge)
3928 .close()
3929 .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3931 .close();
3932
3933 // coverage test: XChainClaim::preclaim - isLockingChain = true;
3934 XEnv(*this)
3936 .close()
3937 .tx(xchain_claim(mcAlice, jvb, 1, XRP(1000), mcBob),
3939 }
3940
3941 void
3943 {
3944 using namespace jtx;
3945
3946 testcase("Bridge Create Account");
3947 XRPAmount tx_fee = txFee();
3948
3949 // coverage test: transferHelper() - dst == src
3950 {
3951 XEnv scEnv(*this, true);
3952
3953 auto const amt = XRP(111);
3954 auto const amt_plus_reward = amt + reward;
3955
3958 .close();
3959
3960 Balance door(scEnv, Account::master);
3961
3962 // scEnv.tx(att_create_acct_batch1(1, amt,
3963 // Account::master)).close();
3965 .close();
3966 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
3967 BEAST_EXPECT(
3968 scEnv.claimCount(jvb) == 0); // claim count is one less
3969
3970 // scEnv.tx(att_create_acct_batch2(1, amt,
3971 // Account::master)).close();
3972 scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2, 2))
3973 .close();
3974 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
3975 BEAST_EXPECT(
3976 scEnv.claimCount(jvb) == 1); // claim count was incremented
3977
3978 BEAST_EXPECT(door.diff() == -reward);
3979 }
3980
3981 // Check that creating an account with less than the minimum create
3982 // amount fails.
3983 {
3984 XEnv mcEnv(*this);
3985
3986 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3987
3988 Balance door(mcEnv, mcDoor);
3989 Balance carol(mcEnv, mcCarol);
3990
3991 mcEnv
3993 mcCarol, jvb, scuAlice, XRP(19), reward),
3995 .close();
3996
3997 BEAST_EXPECT(door.diff() == STAmount(0));
3998 BEAST_EXPECT(carol.diff() == -tx_fee);
3999 }
4000
4001 // Check that creating an account with invalid flags fails.
4002 {
4003 XEnv mcEnv(*this);
4004
4005 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4006
4007 Balance door(mcEnv, mcDoor);
4008
4009 mcEnv
4011 mcCarol, jvb, scuAlice, XRP(20), reward),
4014 .close();
4015
4016 BEAST_EXPECT(door.diff() == STAmount(0));
4017 }
4018
4019 // Check that creating an account with the XChainBridge feature
4020 // disabled fails.
4021 {
4022 XEnv mcEnv(*this);
4023
4024 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4025
4026 Balance door(mcEnv, mcDoor);
4027
4028 mcEnv.disableFeature(featureXChainBridge)
4030 mcCarol, jvb, scuAlice, XRP(20), reward),
4032 .close();
4033
4034 BEAST_EXPECT(door.diff() == STAmount(0));
4035 }
4036
4037 // Check that creating an account with a negative amount fails
4038 {
4039 XEnv mcEnv(*this);
4040
4041 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4042
4043 Balance door(mcEnv, mcDoor);
4044
4045 mcEnv
4047 mcCarol, jvb, scuAlice, XRP(-20), reward),
4049 .close();
4050
4051 BEAST_EXPECT(door.diff() == STAmount(0));
4052 }
4053
4054 // Check that creating an account with a negative reward fails
4055 {
4056 XEnv mcEnv(*this);
4057
4058 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4059
4060 Balance door(mcEnv, mcDoor);
4061
4062 mcEnv
4064 mcCarol, jvb, scuAlice, XRP(20), XRP(-1)),
4066 .close();
4067
4068 BEAST_EXPECT(door.diff() == STAmount(0));
4069 }
4070
4071 // Check that door account can't lock funds onto itself
4072 {
4073 XEnv mcEnv(*this);
4074
4075 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4076
4077 Balance door(mcEnv, mcDoor);
4078
4079 mcEnv
4081 mcDoor, jvb, scuAlice, XRP(20), XRP(1)),
4083 .close();
4084
4085 BEAST_EXPECT(door.diff() == -tx_fee);
4086 }
4087
4088 // Check that reward matches the amount specified in bridge
4089 {
4090 XEnv mcEnv(*this);
4091
4092 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4093
4094 Balance door(mcEnv, mcDoor);
4095
4096 mcEnv
4098 mcCarol, jvb, scuAlice, XRP(20), XRP(2)),
4100 .close();
4101
4102 BEAST_EXPECT(door.diff() == STAmount(0));
4103 }
4104 }
4105
4106 void
4108 {
4109 using namespace jtx;
4110 XRPAmount res0 = reserve(0);
4111 XRPAmount tx_fee = txFee();
4112
4113 testcase("Fee dips into reserve");
4114
4115 // commit where the fee dips into the reserve, this should succeed
4116 XEnv(*this)
4118 .fund(res0 + one_xrp + tx_fee - drops(1), mcuAlice)
4119 .close()
4121 ter(tesSUCCESS));
4122
4123 // commit where the commit amount drips into the reserve, this should
4124 // fail
4125 XEnv(*this)
4127 .fund(res0 + one_xrp - drops(1), mcuAlice)
4128 .close()
4131
4132 auto const minAccountCreate = XRP(20);
4133
4134 // account create commit where the fee dips into the reserve,
4135 // this should succeed
4136 XEnv(*this)
4137 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4138 .fund(
4139 res0 + tx_fee + minAccountCreate + reward - drops(1), mcuAlice)
4140 .close()
4142 mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4143 ter(tesSUCCESS));
4144
4145 // account create commit where the commit dips into the reserve,
4146 // this should fail
4147 XEnv(*this)
4148 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4149 .fund(res0 + minAccountCreate + reward - drops(1), mcuAlice)
4150 .close()
4152 mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4154 }
4155
4156 void
4158 {
4159 using namespace jtx;
4160
4161 testcase("Bridge Delete Door Account");
4162
4163 auto const acctDelFee{
4164 drops(XEnv(*this).env_.current()->fees().increment)};
4165
4166 // Deleting an account that owns bridge should fail
4167 {
4168 XEnv mcEnv(*this);
4169
4170 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1))).close();
4171
4172 // We don't allow an account to be deleted if its sequence
4173 // number is within 256 of the current ledger.
4174 for (size_t i = 0; i < 256; ++i)
4175 mcEnv.close();
4176
4177 // try to delete mcDoor, send funds to mcAlice
4178 mcEnv.tx(
4180 fee(acctDelFee),
4182 }
4183
4184 // Deleting an account that owns a claim id should fail
4185 {
4186 XEnv scEnv(*this, true);
4187
4189 .close()
4191 .close();
4192
4193 // We don't allow an account to be deleted if its sequence
4194 // number is within 256 of the current ledger.
4195 for (size_t i = 0; i < 256; ++i)
4196 scEnv.close();
4197
4198 // try to delete scAlice, send funds to scBob
4199 scEnv.tx(
4201 fee(acctDelFee),
4203 }
4204 }
4205
4206 void
4208 {
4209 using namespace jtx;
4210
4211 testcase("Bad attestations");
4212 {
4213 // Create a bridge and add an attestation with a bad public key
4214 XEnv scEnv(*this, true);
4215 std::uint32_t const claimID = 1;
4217 auto const amt = XRP(1000);
4220 .close();
4222 .close();
4223 auto jvAtt = claim_attestation(
4224 scAttester,
4225 jvb,
4226 mcAlice,
4227 amt,
4229 true,
4230 claimID,
4231 dst,
4233 {
4234 // Change to an invalid keytype
4235 auto k = jvAtt["PublicKey"].asString();
4236 k.at(1) = '9';
4237 jvAtt["PublicKey"] = k;
4238 }
4239 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
4240 }
4241 {
4242 // Create a bridge and add an create account attestation with a bad
4243 // public key
4244 XEnv scEnv(*this, true);
4245 std::uint32_t const createCount = 1;
4246 Account dst{scBob};
4247 auto const amt = XRP(1000);
4248 auto const rewardAmt = XRP(1);
4251 .close();
4252 auto jvAtt = create_account_attestation(
4253 scAttester,
4254 jvb,
4255 mcAlice,
4256 amt,
4257 rewardAmt,
4259 true,
4260 createCount,
4261 dst,
4263 {
4264 // Change to an invalid keytype
4265 auto k = jvAtt["PublicKey"].asString();
4266 k.at(1) = '9';
4267 jvAtt["PublicKey"] = k;
4268 }
4269 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
4270 }
4271 }
4272
4273 void
4292};
4293
4294// -----------------------------------------------------------
4295// -----------------------------------------------------------
4298{
4299private:
4300 static constexpr size_t num_signers = 5;
4301
4302 // --------------------------------------------------
4303 enum class WithClaim { no, yes };
4315
4326
4328 using BridgeID = BridgeDef const*;
4329
4330 // tracking chain state
4331 // --------------------
4333 {
4336
4337 void
4338 init(ENV& env, jtx::Account const& acct)
4339 {
4340 startAmount = env.balance(acct);
4342 }
4343
4344 bool
4345 verify(ENV& env, jtx::Account const& acct) const
4346 {
4347 STAmount diff{env.balance(acct) - startAmount};
4348 bool check = diff == expectedDiff;
4349 return check;
4350 }
4351 };
4352
4353 // --------------------------------------------------
4355 {
4359
4361 : env(env), tx_fee(env.env_.current()->fees().base)
4362 {
4363 }
4364
4365 void
4366 sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec& claims)
4367 {
4368 for (auto const& c : claims)
4369 {
4370 env.tx(c).close();
4371 spendFee(bridge->signers[signer_idx].account);
4372 }
4373 claims.clear();
4374 }
4375
4376 uint32_t
4378 size_t signer_idx,
4380 CreateClaimVec& claims)
4381 {
4382 size_t num_successful = 0;
4383 for (auto const& c : claims)
4384 {
4385 env.tx(c).close();
4386 if (env.ter() == tesSUCCESS)
4387 {
4388 counters[bridge].signers.push_back(signer_idx);
4389 num_successful++;
4390 }
4391 spendFee(bridge->signers[signer_idx].account);
4392 }
4393 claims.clear();
4394 return num_successful;
4395 }
4396
4397 void
4399 {
4400 bool callback_called;
4401
4402 // we have this "do {} while" loop because we want to process
4403 // all the account create which can reach quorum at this time
4404 // stamp.
4405 do
4406 {
4407 callback_called = false;
4408 for (size_t i = 0; i < signers_attns.size(); ++i)
4409 {
4410 for (auto& [bridge, claims] : signers_attns[i])
4411 {
4412 sendAttestations(i, bridge, claims.xfer_claims);
4413
4414 auto& c = counters[bridge];
4415 auto& create_claims =
4416 claims.create_claims[c.claim_count];
4417 auto num_attns = create_claims.size();
4418 if (num_attns)
4419 {
4420 c.num_create_attn_sent += sendCreateAttestations(
4421 i, bridge, create_claims);
4422 }
4423 assert(claims.create_claims[c.claim_count].empty());
4424 }
4425 }
4426 for (auto& [bridge, c] : counters)
4427 {
4428 if (c.num_create_attn_sent >= bridge->quorum)
4429 {
4430 callback_called = true;
4431 c.create_callbacks[c.claim_count](c.signers);
4432 ++c.claim_count;
4433 c.num_create_attn_sent = 0;
4434 c.signers.clear();
4435 }
4436 }
4437 } while (callback_called);
4438 }
4439
4440 void
4441 init(jtx::Account const& acct)
4442 {
4443 accounts[acct].init(env, acct);
4444 }
4445
4446 void
4448 jtx::Account const& acct,
4449 STAmount amt,
4450 std::uint64_t divisor = 1)
4451 {
4452 if (amt.issue() != xrpIssue())
4453 return;
4454 auto it = accounts.find(acct);
4455 if (it == accounts.end())
4456 {
4457 accounts[acct].init(env, acct);
4458 // we just looked up the account, so expectedDiff == 0
4459 }
4460 else
4461 {
4462 it->second.expectedDiff +=
4463 (divisor == 1 ? amt
4464 : divide(
4465 amt,
4466 STAmount(amt.issue(), divisor),
4467 amt.issue()));
4468 }
4469 }
4470
4471 void
4472 spend(jtx::Account const& acct, STAmount amt, std::uint64_t times = 1)
4473 {
4474 if (amt.issue() != xrpIssue())
4475 return;
4476 receive(
4477 acct,
4478 times == 1
4479 ? -amt
4480 : -multiply(
4481 amt, STAmount(amt.issue(), times), amt.issue()));
4482 }
4483
4484 void
4485 transfer(jtx::Account const& from, jtx::Account const& to, STAmount amt)
4486 {
4487 spend(from, amt);
4488 receive(to, amt);
4489 }
4490
4491 void
4492 spendFee(jtx::Account const& acct, size_t times = 1)
4493 {
4494 spend(acct, tx_fee, times);
4495 }
4496
4497 bool
4498 verify() const
4499 {
4500 for (auto const& [acct, state] : accounts)
4501 if (!state.verify(env, acct))
4502 return false;
4503 return true;
4504 }
4505
4507 {
4510
4512 uint32_t create_count{0}; // for account create. First should be 1
4514 0}; // for account create. Increments after quorum for
4515 // current create_count (starts at 1) is reached.
4516
4517 uint32_t num_create_attn_sent{0}; // for current claim_count
4520 };
4521
4527
4530
4536 };
4537
4539 {
4540 ChainStateTracker(ENV& a_env, ENV& b_env) : a_(a_env), b_(b_env)
4541 {
4542 }
4543
4544 bool
4545 verify() const
4546 {
4547 return a_.verify() && b_.verify();
4548 }
4549
4550 void
4552 {
4555 }
4556
4557 void
4558 init(jtx::Account const& acct)
4559 {
4560 a_.init(acct);
4561 b_.init(acct);
4562 }
4563
4566 };
4567
4576
4577 enum Act_Flags { af_a2b = 1 << 0 };
4578
4579 // --------------------------------------------------
4580 template <class T>
4582 {
4583 public:
4585 std::shared_ptr<ChainStateTracker> const& chainstate,
4586 BridgeDef const& bridge)
4587 : bridge_(bridge), st_(chainstate)
4588 {
4589 }
4590
4593 {
4594 return static_cast<T&>(*this).a2b() ? st_->a_ : st_->b_;
4595 }
4596
4599 {
4600 return static_cast<T&>(*this).a2b() ? st_->b_ : st_->a_;
4601 }
4602
4603 jtx::Account const&
4605 {
4606 return static_cast<T&>(*this).a2b() ? bridge_.doorA : bridge_.doorB;
4607 }
4608
4609 jtx::Account const&
4611 {
4612 return static_cast<T&>(*this).a2b() ? bridge_.doorB : bridge_.doorA;
4613 }
4614
4615 protected:
4618 };
4619
4620 // --------------------------------------------------
4621 class SmCreateAccount : public SmBase<SmCreateAccount>
4622 {
4623 public:
4625
4627 std::shared_ptr<ChainStateTracker> const& chainstate,
4628 BridgeDef const& bridge,
4630 : Base(chainstate, bridge)
4632 , cr(std::move(create))
4633 {
4634 }
4635
4636 bool
4637 a2b() const
4638 {
4639 return cr.a2b;
4640 }
4641
4642 uint32_t
4644 {
4645 ChainStateTrack& st = srcState();
4646 jtx::Account const& srcdoor = srcDoor();
4647
4648 st.env
4651 .close(); // needed for claim_id sequence to be correct'
4652 st.spendFee(cr.from);
4653 st.transfer(cr.from, srcdoor, cr.amt);
4654 st.transfer(cr.from, srcdoor, cr.reward);
4655
4656 return ++st.counters[&bridge_].create_count;
4657 }
4658
4659 void
4661 {
4662 ChainStateTrack& st = destState();
4663
4664 // check all signers, but start at a random one
4665 size_t i;
4666 for (i = 0; i < num_signers; ++i)
4667 {
4668 size_t signer_idx = (rnd + i) % num_signers;
4669
4670 if (!(cr.attested[signer_idx]))
4671 {
4672 // enqueue one attestation for this signer
4673 cr.attested[signer_idx] = true;
4674
4675 st.signers_attns[signer_idx][&bridge_]
4676 .create_claims[cr.claim_id - 1]
4677 .emplace_back(create_account_attestation(
4678 bridge_.signers[signer_idx].account,
4679 bridge_.jvb,
4680 cr.from,
4681 cr.amt,
4682 cr.reward,
4683 bridge_.signers[signer_idx].account,
4684 cr.a2b,
4685 cr.claim_id,
4686 cr.to,
4687 bridge_.signers[signer_idx]));
4688 break;
4689 }
4690 }
4691
4692 if (i == num_signers)
4693 return; // did not attest
4694
4695 auto& counters = st.counters[&bridge_];
4696 if (counters.create_callbacks.size() < cr.claim_id)
4697 counters.create_callbacks.resize(cr.claim_id);
4698
4699 auto complete_cb = [&](std::vector<size_t> const& signers) {
4700 auto num_attestors = signers.size();
4701 st.env.close();
4702 assert(
4703 num_attestors <=
4704 std::count(cr.attested.begin(), cr.attested.end(), true));
4705 assert(num_attestors >= bridge_.quorum);
4706 assert(cr.claim_id - 1 == counters.claim_count);
4707
4708 auto r = cr.reward;
4709 auto reward = divide(r, STAmount(num_attestors), r.issue());
4710
4711 for (auto i : signers)
4712 st.receive(bridge_.signers[i].account, reward);
4713
4714 st.spend(dstDoor(), reward, num_attestors);
4715 st.transfer(dstDoor(), cr.to, cr.amt);
4716 st.env.env_.memoize(cr.to);
4718 };
4719
4720 counters.create_callbacks[cr.claim_id - 1] = std::move(complete_cb);
4721 }
4722
4723 SmState
4725 {
4726 switch (sm_state)
4727 {
4728 case st_initial:
4731 break;
4732
4733 case st_attesting:
4734 attest(time, rnd);
4735 break;
4736
4737 default:
4738 assert(0);
4739 break;
4740
4741 case st_completed:
4742 break; // will get this once
4743 }
4744 return sm_state;
4745 }
4746
4747 private:
4750 };
4751
4752 // --------------------------------------------------
4753 class SmTransfer : public SmBase<SmTransfer>
4754 {
4755 public:
4757
4759 std::shared_ptr<ChainStateTracker> const& chainstate,
4760 BridgeDef const& bridge,
4761 Transfer xfer)
4762 : Base(chainstate, bridge)
4763 , xfer(std::move(xfer))
4765 {
4766 }
4767
4768 bool
4769 a2b() const
4770 {
4771 return xfer.a2b;
4772 }
4773
4774 uint32_t
4776 {
4777 ChainStateTrack& st = destState();
4778
4779 st.env
4782 .close(); // needed for claim_id sequence to be
4783 // correct'
4784 st.spendFee(xfer.to);
4785 return ++st.counters[&bridge_].claim_id;
4786 }
4787
4788 void
4790 {
4791 ChainStateTrack& st = srcState();
4792 jtx::Account const& srcdoor = srcDoor();
4793
4794 if (xfer.amt.issue() != xrpIssue())
4795 {
4796 st.env.tx(pay(srcdoor, xfer.from, xfer.amt));
4797 st.spendFee(srcdoor);
4798 }
4799 st.env.tx(xchain_commit(
4800 xfer.from,
4801 bridge_.jvb,
4802 xfer.claim_id,
4803 xfer.amt,
4805 ? std::nullopt
4807 st.spendFee(xfer.from);
4808 st.transfer(xfer.from, srcdoor, xfer.amt);
4809 }
4810
4811 void
4813 {
4814 auto r = bridge_.reward;
4815 auto reward = divide(r, STAmount(bridge_.quorum), r.issue());
4816
4817 for (size_t i = 0; i < num_signers; ++i)
4818 {
4819 if (xfer.attested[i])
4820 st.receive(bridge_.signers[i].account, reward);
4821 }
4823 }
4824
4825 bool
4827 {
4828 ChainStateTrack& st = destState();
4829
4830 // check all signers, but start at a random one
4831 for (size_t i = 0; i < num_signers; ++i)
4832 {
4833 size_t signer_idx = (rnd + i) % num_signers;
4834 if (!(xfer.attested[signer_idx]))
4835 {
4836 // enqueue one attestation for this signer
4837 xfer.attested[signer_idx] = true;
4838
4839 st.signers_attns[signer_idx][&bridge_]
4840 .xfer_claims.emplace_back(claim_attestation(
4841 bridge_.signers[signer_idx].account,
4842 bridge_.jvb,
4843 xfer.from,
4844 xfer.amt,
4845 bridge_.signers[signer_idx].account,
4846 xfer.a2b,
4847 xfer.claim_id,
4849 ? std::nullopt
4851 bridge_.signers[signer_idx]));
4852 break;
4853 }
4854 }
4855
4856 // return true if quorum was reached, false otherwise
4857 bool quorum =
4861 {
4864 }
4865 return quorum;
4866 }
4867
4868 void
4870 {
4871 ChainStateTrack& st = destState();
4872 st.env.tx(xchain_claim(
4876 st.spendFee(xfer.to);
4877 }
4878
4879 SmState
4881 {
4882 switch (sm_state)
4883 {
4884 case st_initial:
4887 break;
4888
4889 case st_claimid_created:
4890 commit();
4892 break;
4893
4894 case st_attesting:
4895 sm_state = attest(time, rnd)
4897 : st_completed)
4898 : st_attesting;
4899 break;
4900
4901 case st_attested:
4902 assert(xfer.with_claim == WithClaim::yes);
4903 claim();
4905 break;
4906
4907 default:
4908 case st_completed:
4909 assert(0); // should have been removed
4910 break;
4911 }
4912 return sm_state;
4913 }
4914
4915 private:
4918 };
4919
4920 // --------------------------------------------------
4923
4925
4926 void
4928 uint64_t time,
4929 std::shared_ptr<ChainStateTracker> const& chainstate,
4930 BridgeDef const& bridge,
4931 Transfer transfer)
4932 {
4934 time, SmTransfer(chainstate, bridge, std::move(transfer)));
4935 }
4936
4937 void
4939 std::shared_ptr<ChainStateTracker> const& chainstate,
4940 BridgeDef const& bridge,
4942 {
4944 time, SmCreateAccount(chainstate, bridge, std::move(ac)));
4945 }
4946
4947public:
4948 void
4951 bool verify_balances = true)
4952 {
4953 using namespace jtx;
4954 uint64_t time = 0;
4955 std::mt19937 gen(27); // Standard mersenne_twister_engine
4957
4958 while (!sm_.empty())
4959 {
4960 ++time;
4961 for (auto it = sm_.begin(); it != sm_.end();)
4962 {
4963 auto vis = [&](auto& sm) {
4964 uint32_t rnd = distrib(gen);
4965 return sm.advance(time, rnd);
4966 };
4967 auto& [t, sm] = *it;
4968 if (t <= time && std::visit(vis, sm) == st_completed)
4969 it = sm_.erase(it);
4970 else
4971 ++it;
4972 }
4973
4974 // send attestations
4975 st->sendAttestations();
4976
4977 // make sure all transactions have been applied
4978 st->a_.env.close();
4979 st->b_.env.close();
4980
4981 if (verify_balances)
4982 {
4983 BEAST_EXPECT(st->verify());
4984 }
4985 }
4986 }
4987
4988 void
4990 {
4991 using namespace jtx;
4992
4993 testcase("Bridge usage simulation");
4994
4995 XEnv mcEnv(*this);
4996 XEnv scEnv(*this, true);
4997
4998 auto st = std::make_shared<ChainStateTracker>(mcEnv, scEnv);
4999
5000 // create 10 accounts + door funded on both chains, and store
5001 // in ChainStateTracker the initial amount of these accounts
5002 Account doorXRPLocking("doorXRPLocking"),
5003 doorUSDLocking("doorUSDLocking"), doorUSDIssuing("doorUSDIssuing");
5004
5005 constexpr size_t num_acct = 10;
5006 auto a = [&doorXRPLocking, &doorUSDLocking, &doorUSDIssuing]() {
5007 using namespace std::literals;
5008 std::vector<Account> result;
5009 result.reserve(num_acct);
5010 for (int i = 0; i < num_acct; ++i)
5011 result.emplace_back(
5012 "a"s + std::to_string(i),
5014 result.emplace_back("doorXRPLocking");
5015 doorXRPLocking = result.back();
5016 result.emplace_back("doorUSDLocking");
5017 doorUSDLocking = result.back();
5018 result.emplace_back("doorUSDIssuing");
5019 doorUSDIssuing = result.back();
5020 return result;
5021 }();
5022
5023 for (auto& acct : a)
5024 {
5025 STAmount amt{XRP(100000)};
5026
5027 mcEnv.fund(amt, acct);
5028 scEnv.fund(amt, acct);
5029 }
5030 Account USDLocking{"USDLocking"};
5031 IOU usdLocking{USDLocking["USD"]};
5032 IOU usdIssuing{doorUSDIssuing["USD"]};
5033
5034 mcEnv.fund(XRP(100000), USDLocking);
5035 mcEnv.close();
5036 mcEnv.tx(trust(doorUSDLocking, usdLocking(100000)));
5037 mcEnv.close();
5038 mcEnv.tx(pay(USDLocking, doorUSDLocking, usdLocking(50000)));
5039
5040 for (int i = 0; i < a.size(); ++i)
5041 {
5042 auto& acct{a[i]};
5043 if (i < num_acct)
5044 {
5045 mcEnv.tx(trust(acct, usdLocking(100000)));
5046 scEnv.tx(trust(acct, usdIssuing(100000)));
5047 }
5048 st->init(acct);
5049 }
5050 for (auto& s : signers)
5051 st->init(s.account);
5052
5053 st->b_.init(Account::master);
5054
5055 // also create some unfunded accounts
5056 constexpr size_t num_ua = 20;
5057 auto ua = []() {
5058 using namespace std::literals;
5059 std::vector<Account> result;
5060 result.reserve(num_ua);
5061 for (int i = 0; i < num_ua; ++i)
5062 result.emplace_back(
5063 "ua"s + std::to_string(i),
5065 return result;
5066 }();
5067
5068 // initialize a bridge from a BridgeDef
5069 auto initBridge = [&mcEnv, &scEnv, &st](BridgeDef& bd) {
5070 bd.initBridge(mcEnv, scEnv);
5071 st->a_.spendFee(bd.doorA, 2);
5072 st->b_.spendFee(bd.doorB, 2);
5073 };
5074
5075 // create XRP -> XRP bridge
5076 // ------------------------
5077 BridgeDef xrp_b{
5078 doorXRPLocking,
5079 xrpIssue(),
5081 xrpIssue(),
5082 XRP(1),
5083 XRP(20),
5084 quorum,
5085 signers,
5087
5088 initBridge(xrp_b);
5089
5090 // create USD -> USD bridge
5091 // ------------------------
5092 BridgeDef usd_b{
5093 doorUSDLocking,
5094 usdLocking,
5095 doorUSDIssuing,
5096 usdIssuing,
5097 XRP(1),
5098 XRP(20),
5099 quorum,
5100 signers,
5102
5103 initBridge(usd_b);
5104
5105 // try a single account create + transfer to validate the simulation
5106 // engine. Do the transfer 8 time steps after the account create, to
5107 // give time enough for ua[0] to be funded now so it can reserve
5108 // the claimID
5109 // -----------------------------------------------------------------
5110 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, true});
5111 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), true});
5112 runSimulation(st);
5113
5114 // try the same thing in the other direction
5115 // -----------------------------------------
5116 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, false});
5117 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), false});
5118 runSimulation(st);
5119
5120 // run multiple XRP transfers
5121 // --------------------------
5122 xfer(0, st, xrp_b, {a[0], a[0], a[1], XRP(6), true, WithClaim::no});
5123 xfer(1, st, xrp_b, {a[0], a[0], a[1], XRP(8), false, WithClaim::no});
5124 xfer(1, st, xrp_b, {a[1], a[1], a[1], XRP(1), true});
5125 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(3), false});
5126 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(5), false});
5127 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(7), false, WithClaim::no});
5128 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(9), true});
5129 runSimulation(st);
5130
5131 // run one USD transfer
5132 // --------------------
5133 xfer(0, st, usd_b, {a[0], a[1], a[2], usdLocking(3), true});
5134 runSimulation(st);
5135
5136 // run multiple USD transfers
5137 // --------------------------
5138 xfer(0, st, usd_b, {a[0], a[0], a[1], usdLocking(6), true});
5139 xfer(1, st, usd_b, {a[0], a[0], a[1], usdIssuing(8), false});
5140 xfer(1, st, usd_b, {a[1], a[1], a[1], usdLocking(1), true});
5141 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(3), false});
5142 xfer(2, st, usd_b, {a[1], a[1], a[1], usdIssuing(5), false});
5143 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(7), false});
5144 xfer(2, st, usd_b, {a[1], a[1], a[1], usdLocking(9), true});
5145 runSimulation(st);
5146
5147 // run mixed transfers
5148 // -------------------
5149 xfer(0, st, xrp_b, {a[0], a[0], a[0], XRP(1), true});
5150 xfer(0, st, usd_b, {a[1], a[3], a[3], usdIssuing(3), false});
5151 xfer(0, st, usd_b, {a[3], a[2], a[1], usdIssuing(5), false});
5152
5153 xfer(1, st, xrp_b, {a[0], a[0], a[0], XRP(4), false});
5154 xfer(1, st, xrp_b, {a[1], a[1], a[0], XRP(8), true});
5155 xfer(1, st, usd_b, {a[4], a[1], a[1], usdLocking(7), true});
5156
5157 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(7), true});
5158 xfer(3, st, xrp_b, {a[0], a[4], a[3], XRP(2), false});
5159 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(9), true});
5160 xfer(3, st, usd_b, {a[3], a[1], a[1], usdIssuing(11), false});
5161 runSimulation(st);
5162
5163 // run multiple account create to stress attestation batching
5164 // ----------------------------------------------------------
5165 ac(0, st, xrp_b, {a[0], ua[1], XRP(301), xrp_b.reward, true});
5166 ac(0, st, xrp_b, {a[1], ua[2], XRP(302), xrp_b.reward, true});
5167 ac(1, st, xrp_b, {a[0], ua[3], XRP(303), xrp_b.reward, true});
5168 ac(2, st, xrp_b, {a[1], ua[4], XRP(304), xrp_b.reward, true});
5169 ac(3, st, xrp_b, {a[0], ua[5], XRP(305), xrp_b.reward, true});
5170 ac(4, st, xrp_b, {a[1], ua[6], XRP(306), xrp_b.reward, true});
5171 ac(6, st, xrp_b, {a[0], ua[7], XRP(307), xrp_b.reward, true});
5172 ac(7, st, xrp_b, {a[2], ua[8], XRP(308), xrp_b.reward, true});
5173 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5174 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5175 ac(10, st, xrp_b, {a[0], ua[10], XRP(310), xrp_b.reward, true});
5176 ac(12, st, xrp_b, {a[0], ua[11], XRP(311), xrp_b.reward, true});
5177 ac(12, st, xrp_b, {a[3], ua[12], XRP(312), xrp_b.reward, true});
5178 ac(12, st, xrp_b, {a[4], ua[13], XRP(313), xrp_b.reward, true});
5179 ac(12, st, xrp_b, {a[3], ua[14], XRP(314), xrp_b.reward, true});
5180 ac(12, st, xrp_b, {a[6], ua[15], XRP(315), xrp_b.reward, true});
5181 ac(13, st, xrp_b, {a[7], ua[16], XRP(316), xrp_b.reward, true});
5182 ac(15, st, xrp_b, {a[3], ua[17], XRP(317), xrp_b.reward, true});
5183 runSimulation(st, true); // balances verification working now.
5184 }
5185
5186 void
5187 run() override
5188 {
5190 }
5191};
5192
5193BEAST_DEFINE_TESTSUITE(XChain, app, ripple);
5194BEAST_DEFINE_TESTSUITE(XChainSim, app, ripple);
5195
5196} // namespace ripple::test
T all_of(T... args)
T apply(T... args)
T back(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:149
A testsuite class.
Definition suite.h:55
void pass()
Record a successful test condition.
Definition suite.h:511
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
virtual Config & config()=0
std::unordered_set< uint256, beast::uhash<> > features
Definition Config.h:276
A currency issued by an account.
Definition Issue.h:33
Issue const & issue() const
Definition STAmount.h:496
static constexpr TERSubset fromInt(int from)
Definition TER.h:429
std::shared_ptr< ChainStateTracker > st_
SmBase(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge)
SmCreateAccount(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, AccountCreate create)
SmState advance(uint64_t time, uint32_t rnd)
void attest(uint64_t time, uint32_t rnd)
void distribute_reward(ChainStateTrack &st)
SmTransfer(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer xfer)
SmState advance(uint64_t time, uint32_t rnd)
bool attest(uint64_t time, uint32_t rnd)
Immutable cryptographic account descriptor.
Definition Account.h:39
static Account const master
The master account.
Definition Account.h:48
A transaction testing environment.
Definition Env.h:121
TER ter() const
Return the TER for the last JTx.
Definition Env.h:595
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:331
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:121
void enableFeature(uint256 const feature)
Definition Env.cpp:650
Application & app()
Definition Env.h:261
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:279
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:183
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:156
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:267
Converts to IOU Issue or STAmount.
Set the fee on a JTx.
Definition fee.h:37
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:35
Set the flags on a JTx.
Definition txflags.h:31
T clear(T... args)
T count(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T front(T... args)
T is_same_v
T make_pair(T... args)
@ nullValue
'null' value
Definition json_value.h:38
Severity
Severity level / threshold of a Journal message.
Definition Journal.h:32
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:486
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition Indexes.cpp:473
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:500
constexpr std::size_t UT_XCHAIN_DEFAULT_QUORUM
Json::Value create_account_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, jtx::signer const &signer)
JValueVec claim_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
Json::Value bridge_create(Account const &acc, Json::Value const &bridge, STAmount const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value claim_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, jtx::signer const &signer)
Json::Value bridge(Account const &lockingChainDoor, Issue const &lockingChainIssue, Account const &issuingChainDoor, Issue const &issuingChainIssue)
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:29
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:34
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:32
std::vector< Json::Value > JValueVec
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:29
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
Json::Value xchain_claim(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, Account const &dst)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:30
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:54
FeatureBitset testable_amendments()
Definition Env.h:74
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
Json::Value bridge_modify(Account const &acc, Json::Value const &bridge, std::optional< STAmount > const &reward, std::optional< STAmount > const &minAccountCreate)
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)
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
Json::Value xchain_commit(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, std::optional< Account > const &dst)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:105
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:115
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:93
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:85
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:77
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
constexpr std::uint32_t tfFillOrKill
Definition TxFlags.h:100
@ current
This was a new validation and was added.
constexpr std::uint32_t asfDisableMaster
Definition TxFlags.h:80
std::string transToken(TER code)
Definition TER.cpp:264
constexpr std::uint32_t tfClearAccountCreateAmount
Definition TxFlags.h:230
@ tecNO_ENTRY
Definition TER.h:306
@ tecNO_DST
Definition TER.h:290
@ tecXCHAIN_SELF_COMMIT
Definition TER.h:350
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition TER.h:346
@ tecXCHAIN_CREATE_ACCOUNT_DISABLED
Definition TER.h:352
@ tecNO_ISSUER
Definition TER.h:299
@ tecXCHAIN_NO_CLAIM_ID
Definition TER.h:337
@ tecXCHAIN_CLAIM_NO_QUORUM
Definition TER.h:339
@ tecXCHAIN_BAD_CLAIM_ID
Definition TER.h:338
@ tecDUPLICATE
Definition TER.h:315
@ tecXCHAIN_ACCOUNT_CREATE_PAST
Definition TER.h:347
@ tecNO_PERMISSION
Definition TER.h:305
@ tecDST_TAG_NEEDED
Definition TER.h:309
@ tecHAS_OBLIGATIONS
Definition TER.h:317
@ tecXCHAIN_REWARD_MISMATCH
Definition TER.h:343
@ tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
Definition TER.h:351
@ tecUNFUNDED_PAYMENT
Definition TER.h:285
@ tecXCHAIN_NO_SIGNERS_LIST
Definition TER.h:344
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tesSUCCESS
Definition TER.h:244
@ terNO_LINE
Definition TER.h:219
@ temBAD_ISSUER
Definition TER.h:93
@ temBAD_AMOUNT
Definition TER.h:89
@ temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
Definition TER.h:135
@ temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
Definition TER.h:136
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition TER.h:131
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
Definition TER.h:134
@ temMALFORMED
Definition TER.h:87
@ temINVALID_FLAG
Definition TER.h:111
@ temXCHAIN_BRIDGE_BAD_ISSUES
Definition TER.h:133
@ temDISABLED
Definition TER.h:114
STL namespace.
T reserve(T... args)
T size(T... args)
bool payees_received(STAmount const &reward) const
std::vector< balance > reward_accounts
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payor, jtx::Account const *payees, size_t num_payees, bool withClaim)
bool check_most_balances(STAmount const &amt, STAmount const &reward)
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payor, std::vector< jtx::Account > const &payees, bool withClaim)
bool has_happened(STAmount const &amt, STAmount const &reward, bool check_payer=true)
Balance(T &env, jtx::Account const &account)
jtx::Account const & account_
STAmount diff() const
void initBridge(ENV &mcEnv, ENV &scEnv)
std::vector< jtx::signer > const & signers
std::shared_ptr< SLE const > bridge(Json::Value const &jvb)
SEnv(T &s, std::unique_ptr< Config > config, FeatureBitset features, std::unique_ptr< Logs > logs=nullptr, beast::severities::Severity thresh=beast::severities::kError)
SEnv & enableFeature(uint256 const feature)
std::uint64_t claimCount(Json::Value const &jvb)
std::shared_ptr< SLE const > account(jtx::Account const &account)
SEnv & multiTx(jtx::JValueVec &&jvv, FN const &... fN)
std::shared_ptr< SLE const > caClaimID(Json::Value const &jvb, std::uint64_t seq)
std::uint64_t claimID(Json::Value const &jvb)
std::shared_ptr< SLE const > claimID(Json::Value const &jvb, std::uint64_t seq)
SEnv & tx(JsonValue &&jv, FN const &... fN)
XRPAmount reserve(std::uint32_t count)
STAmount balance(jtx::Account const &account) const
SEnv & disableFeature(uint256 const feature)
STAmount balance(jtx::Account const &account, Issue const &issue) const
SEnv & fund(STAmount const &amount, Arg const &arg, Args const &... args)
std::array< bool, num_signers > attested
void init(ENV &env, jtx::Account const &acct)
bool verify(ENV &env, jtx::Account const &acct) const
void spend(jtx::Account const &acct, STAmount amt, std::uint64_t times=1)
void transfer(jtx::Account const &from, jtx::Account const &to, STAmount amt)
uint32_t sendCreateAttestations(size_t signer_idx, BridgeID bridge, CreateClaimVec &claims)
void spendFee(jtx::Account const &acct, size_t times=1)
std::map< BridgeID, BridgeCounters > counters
void sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec &claims)
void receive(jtx::Account const &acct, STAmount amt, std::uint64_t divisor=1)
std::map< jtx::Account, AccountStateTrack > accounts
std::array< bool, num_signers > attested
void ac(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, AccountCreate ac)
void xfer(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer transfer)
void run() override
Runs the suite.
static constexpr size_t num_signers
void runSimulation(std::shared_ptr< ChainStateTracker > const &st, bool verify_balances=true)
void run() override
Runs the suite.
void testXChainAddAccountCreateNonBatchAttestation()
XRPAmount reserve(std::uint32_t count)
XEnv(T &s, bool side=false)
JValueVec att_create_acct_vec(std::uint64_t createCount, jtx::AnyAmount const &amt, jtx::Account const &dst, std::size_t const numAtts, std::size_t const fromIdx=0)
std::vector< signer > const alt_signers
Json::Value create_bridge(Account const &acc, Json::Value const &bridge=Json::nullValue, STAmount const &_reward=XRP(1), std::optional< STAmount > const &minAccountCreate=std::nullopt)
std::vector< Account > const payees
std::vector< signer > const signers
Set the sequence number on a JTx.
Definition seq.h:34
A signer in a SignerList.
Definition multisign.h:39
T tmpnam(T... args)
T to_string(T... args)
T visit(T... args)