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