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. This is not allowed.
476  // Although it doesn't violate any invariants, it's not a useful thing
477  // to do and it complicates the "add claim" transactions.
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  ter(tecDUPLICATE))
485  .close();
486 
487  // Create a bridge on an account with exactly enough balance to
488  // meet the new reserve should succeed
489  XEnv(*this)
490  .fund(res1, mcuDoor) // exact reserve for account + 1 object
491  .close()
493 
494  // Create a bridge on an account with no enough balance to meet the
495  // new reserve
496  XEnv(*this)
497  .fund(res1 - 1, mcuDoor) // just short of required reserve
498  .close()
500 
501  // Reward amount is non-xrp
502  XEnv(*this).tx(
505 
506  // Reward amount is XRP and negative
507  XEnv(*this).tx(
508  create_bridge(mcDoor, jvb, XRP(-1)),
510 
511  // Reward amount is 1 xrp => should succeed
512  XEnv(*this).tx(create_bridge(mcDoor, jvb, XRP(1)), ter(tesSUCCESS));
513 
514  // Min create amount is 1 xrp, mincreate is 1 xrp => should succeed
515  XEnv(*this).tx(
517 
518  // Min create amount is non-xrp
519  XEnv(*this).tx(
520  create_bridge(mcDoor, jvb, XRP(1), mcUSD(100)),
522 
523  // Min create amount is zero (should fail, currently succeeds)
524  XEnv(*this).tx(
525  create_bridge(mcDoor, jvb, XRP(1), XRP(0)),
527 
528  // Min create amount is negative
529  XEnv(*this).tx(
530  create_bridge(mcDoor, jvb, XRP(1), XRP(-1)),
532 
533  // coverage test: BridgeCreate::preflight() - create bridge when feature
534  // disabled.
535  {
538  }
539 
540  // coverage test: BridgeCreate::preclaim() returns tecNO_ISSUER.
541  XEnv(*this).tx(
543  mcAlice, bridge(mcAlice, mcuAlice["USD"], mcBob, mcBob["USD"])),
544  ter(tecNO_ISSUER));
545 
546  // coverage test: create_bridge transaction with incorrect flag
547  XEnv(*this).tx(
551 
552  // coverage test: create_bridge transaction with xchain feature disabled
553  XEnv(*this)
554  .disableFeature(featureXChainBridge)
556  }
557 
558  void
560  {
606  using namespace jtx;
607  testcase("Bridge create constraints");
608  XEnv env(*this, true);
609  auto& A = scAlice;
610  auto& B = scBob;
611  auto& C = scCarol;
612  auto AUSD = A["USD"];
613  auto BUSD = B["USD"];
614  auto CUSD = C["USD"];
615  auto GUSD = scGw["USD"];
616  auto AEUR = A["EUR"];
617  auto BEUR = B["EUR"];
618  auto CEUR = C["EUR"];
619  auto GEUR = scGw["EUR"];
620 
621  // Accounts to own single brdiges
622  Account const a1("a1");
623  Account const a2("a2");
624  Account const a3("a3");
625  Account const a4("a4");
626  Account const a5("a5");
627  Account const a6("a6");
628 
629  env.fund(XRP(10000), a1, a2, a3, a4, a5, a6);
630  env.close();
631 
632  // Add a bridge on two different accounts with the same locking and
633  // issuing assets
634  env.tx(create_bridge(a1, bridge(a1, GUSD, B, BUSD))).close();
635  env.tx(create_bridge(a2, bridge(a2, GUSD, B, BUSD))).close();
636 
637  // Add the exact same bridge to two different accounts (one locking
638  // account and one issuing)
639  env.tx(create_bridge(a3, bridge(a3, GUSD, a4, a4["USD"]))).close();
640  env.tx(create_bridge(a4, bridge(a3, GUSD, a4, a4["USD"])),
641  ter(tecDUPLICATE))
642  .close();
643 
644  // Add the exact same bridge to two different accounts (one issuing
645  // account and one locking - opposite order from the test above)
646  env.tx(create_bridge(a5, bridge(a6, GUSD, a5, a5["USD"]))).close();
647  env.tx(create_bridge(a6, bridge(a6, GUSD, a5, a5["USD"])),
648  ter(tecDUPLICATE))
649  .close();
650 
651  // Test case 1 ~ 5, create bridges
652  auto const goodBridge1 = bridge(A, GUSD, B, BUSD);
653  auto const goodBridge2 = bridge(A, BUSD, C, CUSD);
654  env.tx(create_bridge(B, goodBridge1)).close();
655  // Issuing asset is the same, this is a duplicate
656  env.tx(create_bridge(B, bridge(A, GEUR, B, BUSD)), ter(tecDUPLICATE))
657  .close();
658  env.tx(create_bridge(A, goodBridge2), ter(tesSUCCESS)).close();
659  // Locking asset is the same - this is a duplicate
660  env.tx(create_bridge(A, bridge(A, BUSD, B, BEUR)), ter(tecDUPLICATE))
661  .close();
662  // Locking asset is USD - this is a duplicate even tho it has a
663  // different issuer
664  env.tx(create_bridge(A, bridge(A, CUSD, B, BEUR)), ter(tecDUPLICATE))
665  .close();
666 
667  // Test case 6 and 7, commits
668  env.tx(trust(C, BUSD(1000)))
669  .tx(trust(A, BUSD(1000)))
670  .close()
671  .tx(pay(B, C, BUSD(1000)))
672  .close();
673  auto const aBalanceStart = env.balance(A, BUSD);
674  auto const cBalanceStart = env.balance(C, BUSD);
675  env.tx(xchain_commit(C, goodBridge1, 1, BUSD(50))).close();
676  BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(0));
677  BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50));
678  env.tx(xchain_commit(C, goodBridge2, 1, BUSD(60))).close();
679  BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(60));
680  BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50 - 60));
681 
682  // bridge modify test cases
683  env.tx(bridge_modify(B, goodBridge1, XRP(33), std::nullopt)).close();
684  BEAST_EXPECT((*env.bridge(goodBridge1))[sfSignatureReward] == XRP(33));
685  env.tx(bridge_modify(A, goodBridge2, XRP(44), std::nullopt)).close();
686  BEAST_EXPECT((*env.bridge(goodBridge2))[sfSignatureReward] == XRP(44));
687  }
688 
689  void
691  {
692  using namespace jtx;
693  testcase("Create Bridge Matrix");
694 
695  // Test all combinations of the following:`
696  // --------------------------------------
697  // - Locking chain is IOU with locking chain door account as issuer
698  // - Locking chain is IOU with issuing chain door account that
699  // exists on the locking chain as issuer
700  // - Locking chain is IOU with issuing chain door account that does
701  // not exists on the locking chain as issuer
702  // - Locking chain is IOU with non-door account (that exists on the
703  // locking chain ledger) as issuer
704  // - Locking chain is IOU with non-door account (that does not exist
705  // exists on the locking chain ledger) as issuer
706  // - Locking chain is XRP
707  // ---------------------------------------------------------------------
708  // - Issuing chain is IOU with issuing chain door account as the
709  // issuer
710  // - Issuing chain is IOU with locking chain door account (that
711  // exists on the issuing chain ledger) as the issuer
712  // - Issuing chain is IOU with locking chain door account (that does
713  // not exist on the issuing chain ledger) as the issuer
714  // - Issuing chain is IOU with non-door account (that exists on the
715  // issuing chain ledger) as the issuer
716  // - Issuing chain is IOU with non-door account (that does not
717  // exists on the issuing chain ledger) as the issuer
718  // - Issuing chain is XRP and issuing chain door account is not the
719  // root account
720  // - Issuing chain is XRP and issuing chain door account is the root
721  // account
722  // ---------------------------------------------------------------------
723  // That's 42 combinations. The only combinations that should succeed
724  // are:
725  // - Locking chain is any IOU,
726  // - Issuing chain is IOU with issuing chain door account as the
727  // issuer
728  // Locking chain is XRP,
729  // - Issuing chain is XRP with issuing chain is the root account.
730  // ---------------------------------------------------------------------
731  Account a, b;
732  Issue ia, ib;
733 
734  std::tuple lcs{
736  "Locking chain is IOU(locking chain door)",
737  [&](auto& env, bool) {
738  a = mcDoor;
739  ia = mcDoor["USD"];
740  }),
742  "Locking chain is IOU(issuing chain door funded on locking "
743  "chain)",
744  [&](auto& env, bool shouldFund) {
745  a = mcDoor;
746  ia = scDoor["USD"];
747  if (shouldFund)
748  env.fund(XRP(10000), scDoor);
749  }),
751  "Locking chain is IOU(issuing chain door account unfunded "
752  "on locking chain)",
753  [&](auto& env, bool) {
754  a = mcDoor;
755  ia = scDoor["USD"];
756  }),
758  "Locking chain is IOU(bob funded on locking chain)",
759  [&](auto& env, bool) {
760  a = mcDoor;
761  ia = mcGw["USD"];
762  }),
764  "Locking chain is IOU(bob unfunded on locking chain)",
765  [&](auto& env, bool) {
766  a = mcDoor;
767  ia = mcuGw["USD"];
768  }),
769  std::make_pair("Locking chain is XRP", [&](auto& env, bool) {
770  a = mcDoor;
771  ia = xrpIssue();
772  })};
773 
774  std::tuple ics{
776  "Issuing chain is IOU(issuing chain door account)",
777  [&](auto& env, bool) {
778  b = scDoor;
779  ib = scDoor["USD"];
780  }),
782  "Issuing chain is IOU(locking chain door funded on issuing "
783  "chain)",
784  [&](auto& env, bool shouldFund) {
785  b = scDoor;
786  ib = mcDoor["USD"];
787  if (shouldFund)
788  env.fund(XRP(10000), mcDoor);
789  }),
791  "Issuing chain is IOU(locking chain door unfunded on "
792  "issuing chain)",
793  [&](auto& env, bool) {
794  b = scDoor;
795  ib = mcDoor["USD"];
796  }),
798  "Issuing chain is IOU(bob funded on issuing chain)",
799  [&](auto& env, bool) {
800  b = scDoor;
801  ib = mcGw["USD"];
802  }),
804  "Issuing chain is IOU(bob unfunded on issuing chain)",
805  [&](auto& env, bool) {
806  b = scDoor;
807  ib = mcuGw["USD"];
808  }),
810  "Issuing chain is XRP and issuing chain door account is "
811  "not the root account",
812  [&](auto& env, bool) {
813  b = scDoor;
814  ib = xrpIssue();
815  }),
817  "Issuing chain is XRP and issuing chain door account is "
818  "the root account ",
819  [&](auto& env, bool) {
820  b = Account::master;
821  ib = xrpIssue();
822  })};
823 
824  std::vector<std::pair<int, int>> expected_result{
867 
869 
870  auto testcase = [&](auto const& lc, auto const& ic) {
871  XEnv mcEnv(*this);
872  XEnv scEnv(*this, true);
873 
874  lc.second(mcEnv, true);
875  lc.second(scEnv, false);
876 
877  ic.second(mcEnv, false);
878  ic.second(scEnv, true);
879 
880  auto const& expected = expected_result[test_result.size()];
881 
882  mcEnv.tx(
883  create_bridge(a, bridge(a, ia, b, ib)),
884  ter(TER::fromInt(expected.first)));
885  TER mcTER = mcEnv.env_.ter();
886 
887  scEnv.tx(
888  create_bridge(b, bridge(a, ia, b, ib)),
889  ter(TER::fromInt(expected.second)));
890  TER scTER = scEnv.env_.ter();
891 
892  bool pass = mcTER == tesSUCCESS && scTER == tesSUCCESS;
893 
894  test_result.emplace_back(mcTER, scTER, pass);
895  };
896 
897  auto apply_ics = [&](auto const& lc, auto const& ics) {
898  std::apply(
899  [&](auto const&... ic) { (testcase(lc, ic), ...); }, ics);
900  };
901 
902  std::apply([&](auto const&... lc) { (apply_ics(lc, ics), ...); }, lcs);
903 
904 #if GENERATE_MTX_OUTPUT
905  // optional output of matrix results in markdown format
906  // ----------------------------------------------------
907  std::string fname{std::tmpnam(nullptr)};
908  fname += ".md";
909  std::cout << "Markdown output for matrix test: " << fname << "\n";
910 
911  auto print_res = [](auto tup) -> std::string {
912  std::string status = std::string(transToken(std::get<0>(tup))) +
913  " / " + transToken(std::get<1>(tup));
914 
915  if (std::get<2>(tup))
916  return status;
917  else
918  {
919  // red
920  return std::string("`") + status + "`";
921  }
922  };
923 
924  auto output_table = [&](auto print_res) {
925  size_t test_idx = 0;
926  std::string res;
927  res.reserve(10000); // should be enough :-)
928 
929  // first two header lines
930  res += "| `issuing ->` | ";
931  std::apply(
932  [&](auto const&... ic) {
933  ((res += ic.first, res += " | "), ...);
934  },
935  ics);
936  res += "\n";
937 
938  res += "| :--- | ";
939  std::apply(
940  [&](auto const&... ic) {
941  (((void)ic.first, res += ":---: | "), ...);
942  },
943  ics);
944  res += "\n";
945 
946  auto output = [&](auto const& lc, auto const& ic) {
947  res += print_res(test_result[test_idx]);
948  res += " | ";
949  ++test_idx;
950  };
951 
952  auto output_ics = [&](auto const& lc, auto const& ics) {
953  res += "| ";
954  res += lc.first;
955  res += " | ";
956  std::apply(
957  [&](auto const&... ic) { (output(lc, ic), ...); }, ics);
958  res += "\n";
959  };
960 
961  std::apply(
962  [&](auto const&... lc) { (output_ics(lc, ics), ...); }, lcs);
963 
964  return res;
965  };
966 
967  std::ofstream(fname) << output_table(print_res);
968 
969  std::string ter_fname{std::tmpnam(nullptr)};
970  std::cout << "ter output for matrix test: " << ter_fname << "\n";
971 
972  std::ofstream ofs(ter_fname);
973  for (auto& t : test_result)
974  {
975  ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", "
976  << std::string(transToken(std::get<1>(t))) << "}\n,";
977  }
978 #endif
979  }
980 
981  void
983  {
984  using namespace jtx;
985  testcase("Modify Bridge");
986 
987  // Changing a non-existent bridge should fail
988  XEnv(*this).tx(
990  mcAlice,
991  bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]),
992  XRP(2),
993  std::nullopt),
994  ter(tecNO_ENTRY));
995 
996  // must change something
997  // XEnv(*this)
998  // .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
999  // .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(1)),
1000  // ter(temMALFORMED));
1001 
1002  // must change something
1003  XEnv(*this)
1004  .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
1005  .close()
1006  .tx(bridge_modify(mcDoor, jvb, {}, {}), ter(temMALFORMED));
1007 
1008  // Reward amount is non-xrp
1009  XEnv(*this).tx(
1010  bridge_modify(mcDoor, jvb, mcUSD(2), XRP(10)),
1012 
1013  // Reward amount is XRP and negative
1014  XEnv(*this).tx(
1015  bridge_modify(mcDoor, jvb, XRP(-2), XRP(10)),
1017 
1018  // Min create amount is non-xrp
1019  XEnv(*this).tx(
1020  bridge_modify(mcDoor, jvb, XRP(2), mcUSD(10)),
1022 
1023  // Min create amount is zero
1024  XEnv(*this).tx(
1025  bridge_modify(mcDoor, jvb, XRP(2), XRP(0)),
1027 
1028  // Min create amount is negative
1029  XEnv(*this).tx(
1030  bridge_modify(mcDoor, jvb, XRP(2), XRP(-10)),
1032 
1033  // First check the regular claim process (without bridge_modify)
1034  for (auto withClaim : {false, true})
1035  {
1036  XEnv mcEnv(*this);
1037  XEnv scEnv(*this, true);
1038 
1039  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1040 
1043  .close()
1045  .close();
1046 
1047  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1048  auto const amt = XRP(1000);
1049  std::uint32_t const claimID = 1;
1050  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1051 
1052  BalanceTransfer transfer(
1053  scEnv,
1055  scBob,
1056  scAlice,
1057  &payees[0],
1059  withClaim);
1060 
1061  scEnv
1063  scAttester,
1064  jvb,
1065  mcAlice,
1066  amt,
1067  payees,
1068  true,
1069  claimID,
1070  dst,
1071  signers))
1072  .close();
1073 
1074  if (withClaim)
1075  {
1076  BEAST_EXPECT(transfer.has_not_happened());
1077 
1078  // need to submit a claim transactions
1079  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1080  .close();
1081  }
1082 
1083  BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1084  }
1085 
1086  // Check that the reward paid from a claim Id was the reward when
1087  // the claim id was created, not the reward since the bridge was
1088  // modified.
1089  for (auto withClaim : {false, true})
1090  {
1091  XEnv mcEnv(*this);
1092  XEnv scEnv(*this, true);
1093 
1094  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1095 
1098  .close()
1100  .close();
1101 
1102  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1103  auto const amt = XRP(1000);
1104  std::uint32_t const claimID = 1;
1105  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1106 
1107  // Now modify the reward on the bridge
1108  mcEnv.tx(bridge_modify(mcDoor, jvb, XRP(2), XRP(10))).close();
1109  scEnv.tx(bridge_modify(Account::master, jvb, XRP(2), XRP(10)))
1110  .close();
1111 
1112  BalanceTransfer transfer(
1113  scEnv,
1115  scBob,
1116  scAlice,
1117  &payees[0],
1119  withClaim);
1120 
1121  scEnv
1123  scAttester,
1124  jvb,
1125  mcAlice,
1126  amt,
1127  payees,
1128  true,
1129  claimID,
1130  dst,
1131  signers))
1132  .close();
1133 
1134  if (withClaim)
1135  {
1136  BEAST_EXPECT(transfer.has_not_happened());
1137 
1138  // need to submit a claim transactions
1139  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1140  .close();
1141  }
1142 
1143  // make sure the reward accounts indeed received the original
1144  // split reward (1 split 5 ways) instead of the updated 2 XRP.
1145  BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1146  }
1147 
1148  // Check that the signatures used to verify attestations and decide
1149  // if there is a quorum are the current signer's list on the door
1150  // account, not the signer's list that was in effect when the claim
1151  // id was created.
1152  for (auto withClaim : {false, true})
1153  {
1154  XEnv mcEnv(*this);
1155  XEnv scEnv(*this, true);
1156 
1157  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1158 
1161  .close()
1163  .close();
1164 
1165  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1166  auto const amt = XRP(1000);
1167  std::uint32_t const claimID = 1;
1168  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1169 
1170  // change signers - claim should not be processed is the batch
1171  // is signed by original signers
1173  .close();
1174 
1175  BalanceTransfer transfer(
1176  scEnv,
1178  scBob,
1179  scAlice,
1180  &payees[0],
1182  withClaim);
1183 
1184  // submit claim using outdated signers - should fail
1185  scEnv
1186  .multiTx(
1188  scAttester,
1189  jvb,
1190  mcAlice,
1191  amt,
1192  payees,
1193  true,
1194  claimID,
1195  dst,
1196  signers),
1198  .close();
1199  if (withClaim)
1200  {
1201  // need to submit a claim transactions
1202  scEnv
1203  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1205  .close();
1206  }
1207 
1208  // make sure transfer has not happened as we sent attestations
1209  // using outdated signers
1210  BEAST_EXPECT(transfer.has_not_happened());
1211 
1212  // submit claim using current signers - should succeed
1213  scEnv
1215  scAttester,
1216  jvb,
1217  mcAlice,
1218  amt,
1219  payees,
1220  true,
1221  claimID,
1222  dst,
1223  alt_signers))
1224  .close();
1225  if (withClaim)
1226  {
1227  BEAST_EXPECT(transfer.has_not_happened());
1228 
1229  // need to submit a claim transactions
1230  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1231  .close();
1232  }
1233 
1234  // make sure the transfer went through as we sent attestations
1235  // using new signers
1236  BEAST_EXPECT(
1237  transfer.has_happened(amt, split_reward_quorum, false));
1238  }
1239 
1240  // coverage test: bridge_modify transaction with incorrect flag
1241  XEnv(*this)
1242  .tx(create_bridge(mcDoor, jvb))
1243  .close()
1244  .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)),
1246  ter(temINVALID_FLAG));
1247 
1248  // coverage test: bridge_modify transaction with xchain feature
1249  // disabled
1250  XEnv(*this)
1251  .tx(create_bridge(mcDoor, jvb))
1252  .disableFeature(featureXChainBridge)
1253  .close()
1254  .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)), ter(temDISABLED));
1255 
1256  // coverage test: bridge_modify return temSIDECHAIN_NONDOOR_OWNER;
1257  XEnv(*this)
1258  .tx(create_bridge(mcDoor, jvb))
1259  .close()
1260  .tx(bridge_modify(mcAlice, jvb, XRP(1), XRP(2)),
1262 
1269  XEnv(*this)
1270  .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20)))
1271  .close()
1273  mcAlice, jvb, scuAlice, XRP(100), reward))
1274  .close()
1275  .tx(bridge_modify(mcDoor, jvb, {}, XRP(2)),
1277  ter(temMALFORMED))
1278  .close()
1279  .tx(bridge_modify(mcDoor, jvb, XRP(3), {}),
1281  .close()
1283  mcAlice, jvb, scuBob, XRP(100), XRP(3)),
1285  .close();
1286  }
1287 
1288  void
1290  {
1291  using namespace jtx;
1292  XRPAmount res1 = reserve(1);
1293  XRPAmount tx_fee = txFee();
1294 
1295  testcase("Create ClaimID");
1296 
1297  // normal bridge create for sanity check with the exact necessary
1298  // account balance
1299  XEnv(*this, true)
1301  .fund(res1, scuAlice) // acct reserve + 1 object
1302  .close()
1304  .close();
1305 
1306  // check reward not deducted when claim id is created
1307  {
1308  XEnv xenv(*this, true);
1309 
1310  Balance scAlice_bal(xenv, scAlice);
1311 
1314  .close();
1315 
1316  BEAST_EXPECT(scAlice_bal.diff() == -tx_fee);
1317  }
1318 
1319  // Non-existent bridge
1320  XEnv(*this, true)
1322  scAlice,
1323  bridge(mcAlice, mcAlice["USD"], scBob, scBob["USD"]),
1324  reward,
1325  mcAlice),
1326  ter(tecNO_ENTRY))
1327  .close();
1328 
1329  // Creating the new object would put the account below the reserve
1330  XEnv(*this, true)
1332  .fund(res1 - xrp_dust, scuAlice) // barely not enough
1333  .close()
1336  .close();
1337 
1338  // The specified reward doesn't match the reward on the bridge (test
1339  // by giving the reward amount for the other side, as well as a
1340  // completely non-matching reward)
1341  XEnv(*this, true)
1343  .close()
1347  .close();
1348 
1349  // A reward amount that isn't XRP
1350  XEnv(*this, true)
1352  .close()
1355  .close();
1356 
1357  // coverage test: xchain_create_claim_id transaction with incorrect
1358  // flag
1359  XEnv(*this, true)
1361  .close()
1365  .close();
1366 
1367  // coverage test: xchain_create_claim_id transaction with xchain
1368  // feature disabled
1369  XEnv(*this, true)
1371  .disableFeature(featureXChainBridge)
1372  .close()
1374  ter(temDISABLED))
1375  .close();
1376  }
1377 
1378  void
1380  {
1381  using namespace jtx;
1382  XRPAmount res0 = reserve(0);
1383  XRPAmount tx_fee = txFee();
1384 
1385  testcase("Commit");
1386 
1387  // Commit to a non-existent bridge
1388  XEnv(*this).tx(
1390 
1391  // check that reward not deducted when doing the commit
1392  {
1393  XEnv xenv(*this);
1394 
1395  Balance alice_bal(xenv, mcAlice);
1396  auto const amt = XRP(1000);
1397 
1398  xenv.tx(create_bridge(mcDoor, jvb))
1399  .close()
1400  .tx(xchain_commit(mcAlice, jvb, 1, amt, scBob))
1401  .close();
1402 
1403  STAmount claim_cost = amt;
1404  BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee));
1405  }
1406 
1407  // Commit a negative amount
1408  XEnv(*this)
1409  .tx(create_bridge(mcDoor, jvb))
1410  .close()
1411  .tx(xchain_commit(mcAlice, jvb, 1, XRP(-1), scBob),
1412  ter(temBAD_AMOUNT));
1413 
1414  // Commit an amount whose issue that does not match the expected
1415  // issue on the bridge (either LockingChainIssue or
1416  // IssuingChainIssue, depending on the chain).
1417  XEnv(*this)
1418  .tx(create_bridge(mcDoor, jvb))
1419  .close()
1420  .tx(xchain_commit(mcAlice, jvb, 1, mcUSD(100), scBob),
1421  ter(temBAD_ISSUER));
1422 
1423  // Commit an amount that would put the sender below the required
1424  // reserve (if XRP)
1425  XEnv(*this)
1426  .tx(create_bridge(mcDoor, jvb))
1427  .fund(res0 + one_xrp - xrp_dust, mcuAlice) // barely not enough
1428  .close()
1429  .tx(xchain_commit(mcuAlice, jvb, 1, one_xrp, scBob),
1431 
1432  XEnv(*this)
1433  .tx(create_bridge(mcDoor, jvb))
1434  .fund(
1435  res0 + one_xrp + xrp_dust, // "xrp_dust" for tx fees
1436  mcuAlice) // exactly enough => should succeed
1437  .close()
1438  .tx(xchain_commit(mcuAlice, jvb, 1, one_xrp, scBob));
1439 
1440  // Commit an amount above the account's balance (for both XRP and
1441  // IOUs)
1442  XEnv(*this)
1443  .tx(create_bridge(mcDoor, jvb))
1444  .fund(res0, mcuAlice) // barely not enough
1445  .close()
1446  .tx(xchain_commit(mcuAlice, jvb, 1, res0 + one_xrp, scBob),
1448 
1449  auto jvb_USD = bridge(mcDoor, mcUSD, scGw, scUSD);
1450 
1451  // commit sent from iou issuer (mcGw) succeeds - should it?
1452  XEnv(*this)
1453  .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1454  .tx(create_bridge(mcDoor, jvb_USD))
1455  .close()
1456  .tx(xchain_commit(mcGw, jvb_USD, 1, mcUSD(1), scBob));
1457 
1458  // commit to a door account from the door account. This should fail.
1459  XEnv(*this)
1460  .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1461  .tx(create_bridge(mcDoor, jvb_USD))
1462  .close()
1463  .tx(xchain_commit(mcDoor, jvb_USD, 1, mcUSD(1), scBob),
1465 
1466  // commit sent from mcAlice which has no IOU balance => should fail
1467  XEnv(*this)
1468  .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1469  .tx(create_bridge(mcDoor, jvb_USD))
1470  .close()
1471  .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scBob),
1472  ter(terNO_LINE));
1473 
1474  // commit sent from mcAlice which has no IOU balance => should fail
1475  // just changed the destination to scGw (which is the door account
1476  // and may not make much sense)
1477  XEnv(*this)
1478  .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1479  .tx(create_bridge(mcDoor, jvb_USD))
1480  .close()
1481  .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scGw),
1482  ter(terNO_LINE));
1483 
1484  // commit sent from mcAlice which has a IOU balance => should
1485  // succeed
1486  XEnv(*this)
1487  .tx(trust(mcDoor, mcUSD(10000)))
1488  .tx(trust(mcAlice, mcUSD(10000)))
1489  .close()
1490  .tx(pay(mcGw, mcAlice, mcUSD(10)))
1491  .tx(create_bridge(mcDoor, jvb_USD))
1492  .close()
1493  //.tx(pay(mcAlice, mcDoor, mcUSD(10)));
1494  .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(10), scAlice));
1495 
1496  // coverage test: xchain_commit transaction with incorrect flag
1497  XEnv(*this)
1498  .tx(create_bridge(mcDoor))
1499  .close()
1500  .tx(xchain_commit(mcAlice, jvb, 1, one_xrp, scBob),
1502  ter(temINVALID_FLAG));
1503 
1504  // coverage test: xchain_commit transaction with xchain feature
1505  // disabled
1506  XEnv(*this)
1507  .tx(create_bridge(mcDoor))
1508  .disableFeature(featureXChainBridge)
1509  .close()
1510  .tx(xchain_commit(mcAlice, jvb, 1, one_xrp, scBob),
1511  ter(temDISABLED));
1512  }
1513 
1514  void
1516  {
1517  using namespace jtx;
1518 
1519  testcase("Add Attestation");
1520  XRPAmount res0 = reserve(0);
1521  XRPAmount tx_fee = txFee();
1522 
1523  auto multiTtxFee = [&](std::uint32_t m) -> STAmount {
1524  return multiply(tx_fee, STAmount(m), xrpIssue());
1525  };
1526 
1527  // Add an attestation to a claim id that has already reached quorum.
1528  // This should succeed and share in the reward.
1529  // note: this is true only when either:
1530  // 1. dest account is not specified, so transfer requires a claim
1531  // 2. or the extra attestation is sent in the same batch as the
1532  // one reaching quorum
1533  for (auto withClaim : {true})
1534  {
1535  XEnv mcEnv(*this);
1536  XEnv scEnv(*this, true);
1537  std::uint32_t const claimID = 1;
1538 
1539  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1540 
1543  .close()
1545  .close();
1546 
1547  BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1548 
1549  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1550  auto const amt = XRP(1000);
1551  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1552 
1553  BalanceTransfer transfer(
1554  scEnv, Account::master, scBob, scAlice, payees, withClaim);
1555 
1556  scEnv
1558  scAttester,
1559  jvb,
1560  mcAlice,
1561  amt,
1562  payees,
1563  true,
1564  claimID,
1565  dst,
1566  signers,
1568  .close();
1569  scEnv
1571  scAttester,
1572  jvb,
1573  mcAlice,
1574  amt,
1576  true,
1577  claimID,
1578  dst,
1580  .close();
1581 
1582  if (withClaim)
1583  {
1584  BEAST_EXPECT(transfer.has_not_happened());
1585 
1586  // need to submit a claim transactions
1587  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1588  .close();
1589  BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1590  BEAST_EXPECT(scEnv.claimID(jvb) == claimID);
1591  }
1592 
1593  BEAST_EXPECT(transfer.has_happened(amt, split_reward_everyone));
1594  }
1595 
1596  // Test that signature weights are correctly handled. Assign
1597  // signature weights of 1,2,4,4 and a quorum of 7. Check that the
1598  // 4,4 signatures reach a quorum, the 1,2,4, reach a quorum, but the
1599  // 4,2, 4,1 and 1,2 do not.
1600 
1601  // 1,2,4 => should succeed
1602  for (auto withClaim : {false, true})
1603  {
1604  XEnv mcEnv(*this);
1605  XEnv scEnv(*this, true);
1606 
1607  std::uint32_t const quorum_7 = 7;
1608  std::vector<signer> const signers_ = [] {
1609  constexpr int numSigners = 4;
1610  std::uint32_t weights[] = {1, 2, 4, 4};
1611 
1612  std::vector<signer> result;
1613  result.reserve(numSigners);
1614  for (int i = 0; i < numSigners; ++i)
1615  {
1616  using namespace std::literals;
1617  auto const a = Account("signer_"s + std::to_string(i));
1618  result.emplace_back(a, weights[i]);
1619  }
1620  return result;
1621  }();
1622 
1623  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1624 
1626  .tx(jtx::signers(Account::master, quorum_7, signers_))
1627  .close()
1629  .close();
1630  std::uint32_t const claimID = 1;
1631  BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1632 
1633  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1634  auto const amt = XRP(1000);
1635 
1636  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1637 
1638  BalanceTransfer transfer(
1639  scEnv,
1641  scBob,
1642  scAlice,
1643  &payees[0],
1644  3,
1645  withClaim);
1646 
1647  scEnv
1649  scAttester,
1650  jvb,
1651  mcAlice,
1652  amt,
1653  payees,
1654  true,
1655  claimID,
1656  dst,
1657  signers_,
1658  3))
1659  .close();
1660 
1661  if (withClaim)
1662  {
1663  BEAST_EXPECT(transfer.has_not_happened());
1664 
1665  // need to submit a claim transactions
1666  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1667  .close();
1668  }
1669 
1670  BEAST_EXPECT(!scEnv.claimID(jvb, 1)); // claim id deleted
1671 
1672  BEAST_EXPECT(transfer.has_happened(
1673  amt, divide(reward, STAmount(3), reward.issue())));
1674  }
1675 
1676  // 4,4 => should succeed
1677  for (auto withClaim : {false, true})
1678  {
1679  XEnv mcEnv(*this);
1680  XEnv scEnv(*this, true);
1681 
1682  std::uint32_t const quorum_7 = 7;
1683  std::vector<signer> const signers_ = [] {
1684  constexpr int numSigners = 4;
1685  std::uint32_t weights[] = {1, 2, 4, 4};
1686 
1687  std::vector<signer> result;
1688  result.reserve(numSigners);
1689  for (int i = 0; i < numSigners; ++i)
1690  {
1691  using namespace std::literals;
1692  auto const a = Account("signer_"s + std::to_string(i));
1693  result.emplace_back(a, weights[i]);
1694  }
1695  return result;
1696  }();
1697  STAmount const split_reward_ =
1698  divide(reward, STAmount(signers_.size()), reward.issue());
1699 
1700  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1701 
1703  .tx(jtx::signers(Account::master, quorum_7, signers_))
1704  .close()
1706  .close();
1707  std::uint32_t const claimID = 1;
1708  BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1709 
1710  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1711  auto const amt = XRP(1000);
1712 
1713  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1714 
1715  BalanceTransfer transfer(
1716  scEnv,
1718  scBob,
1719  scAlice,
1720  &payees[2],
1721  2,
1722  withClaim);
1723 
1724  scEnv
1726  scAttester,
1727  jvb,
1728  mcAlice,
1729  amt,
1730  payees,
1731  true,
1732  claimID,
1733  dst,
1734  signers_,
1735  2,
1736  2))
1737  .close();
1738 
1739  if (withClaim)
1740  {
1741  BEAST_EXPECT(transfer.has_not_happened());
1742 
1743  // need to submit a claim transactions
1744  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1745  .close();
1746  }
1747 
1748  BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1749 
1750  BEAST_EXPECT(transfer.has_happened(
1751  amt, divide(reward, STAmount(2), reward.issue())));
1752  }
1753 
1754  // 1,2 => should fail
1755  for (auto withClaim : {false, true})
1756  {
1757  XEnv mcEnv(*this);
1758  XEnv scEnv(*this, true);
1759 
1760  std::uint32_t const quorum_7 = 7;
1761  std::vector<signer> const signers_ = [] {
1762  constexpr int numSigners = 4;
1763  std::uint32_t weights[] = {1, 2, 4, 4};
1764 
1765  std::vector<signer> result;
1766  result.reserve(numSigners);
1767  for (int i = 0; i < numSigners; ++i)
1768  {
1769  using namespace std::literals;
1770  auto const a = Account("signer_"s + std::to_string(i));
1771  result.emplace_back(a, weights[i]);
1772  }
1773  return result;
1774  }();
1775 
1776  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1777 
1779  .tx(jtx::signers(Account::master, quorum_7, signers_))
1780  .close()
1782  .close();
1783 
1784  std::uint32_t const claimID = 1;
1785  BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1786 
1787  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1788  auto const amt = XRP(1000);
1789  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1790 
1791  BalanceTransfer transfer(
1792  scEnv,
1794  scBob,
1795  scAlice,
1796  &payees[0],
1797  2,
1798  withClaim);
1799 
1800  scEnv
1802  scAttester,
1803  jvb,
1804  mcAlice,
1805  amt,
1806  payees,
1807  true,
1808  claimID,
1809  dst,
1810  signers_,
1811  2))
1812  .close();
1813  if (withClaim)
1814  {
1815  BEAST_EXPECT(transfer.has_not_happened());
1816 
1817  // need to submit a claim transactions
1818  scEnv
1819  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1821  .close();
1822  }
1823 
1824  BEAST_EXPECT(
1825  !!scEnv.claimID(jvb, claimID)); // claim id still present
1826  BEAST_EXPECT(transfer.has_not_happened());
1827  }
1828 
1829  // 2,4 => should fail
1830  for (auto withClaim : {false, true})
1831  {
1832  XEnv mcEnv(*this);
1833  XEnv scEnv(*this, true);
1834 
1835  std::uint32_t const quorum_7 = 7;
1836  std::vector<signer> const signers_ = [] {
1837  constexpr int numSigners = 4;
1838  std::uint32_t weights[] = {1, 2, 4, 4};
1839 
1840  std::vector<signer> result;
1841  result.reserve(numSigners);
1842  for (int i = 0; i < numSigners; ++i)
1843  {
1844  using namespace std::literals;
1845  auto const a = Account("signer_"s + std::to_string(i));
1846  result.emplace_back(a, weights[i]);
1847  }
1848  return result;
1849  }();
1850 
1851  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1852 
1854  .tx(jtx::signers(Account::master, quorum_7, signers_))
1855  .close()
1857  .close();
1858 
1859  std::uint32_t const claimID = 1;
1860  BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1861 
1862  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1863  auto const amt = XRP(1000);
1864 
1865  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1866 
1867  BalanceTransfer transfer(
1868  scEnv,
1870  scBob,
1871  scAlice,
1872  &payees[1],
1873  2,
1874  withClaim);
1875 
1876  scEnv
1878  scAttester,
1879  jvb,
1880  mcAlice,
1881  amt,
1882  payees,
1883  true,
1884  claimID,
1885  dst,
1886  signers_,
1887  2,
1888  1))
1889  .close();
1890 
1891  if (withClaim)
1892  {
1893  BEAST_EXPECT(transfer.has_not_happened());
1894 
1895  // need to submit a claim transactions
1896  scEnv
1897  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1899  .close();
1900  }
1901 
1902  BEAST_EXPECT(
1903  !!scEnv.claimID(jvb, claimID)); // claim id still present
1904  BEAST_EXPECT(transfer.has_not_happened());
1905  }
1906 
1907  // Confirm that account create transactions happen in the correct
1908  // order. If they reach quorum out of order they should not execute
1909  // until all the previous created transactions have occurred.
1910  // Re-adding an attestation should move funds.
1911  {
1912  XEnv mcEnv(*this);
1913  XEnv scEnv(*this, true);
1914  auto const amt = XRP(1000);
1915  auto const amt_plus_reward = amt + reward;
1916 
1917  {
1918  Balance door(mcEnv, mcDoor);
1919  Balance carol(mcEnv, mcCarol);
1920 
1921  mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1922  .close()
1924  mcAlice, jvb, scuAlice, amt, reward))
1926  mcBob, jvb, scuBob, amt, reward))
1928  mcCarol, jvb, scuCarol, amt, reward))
1929  .close();
1930 
1931  BEAST_EXPECT(
1932  door.diff() ==
1933  (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
1934  tx_fee));
1935  BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1936  }
1937 
1938  scEnv.tx(create_bridge(Account::master, jvb, reward, XRP(20)))
1940  .close();
1941 
1942  {
1943  // send first batch of account create attest for all 3
1944  // account create
1945  Balance attester(scEnv, scAttester);
1946  Balance door(scEnv, Account::master);
1947 
1948  scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2))
1949  .multiTx(att_create_acct_vec(3, amt, scuCarol, 2))
1950  .multiTx(att_create_acct_vec(2, amt, scuBob, 2))
1951  .close();
1952 
1953  BEAST_EXPECT(door.diff() == STAmount(0));
1954  // att_create_acct_vec return vectors of size 2, so 2*3 txns
1955  BEAST_EXPECT(attester.diff() == -multiTtxFee(6));
1956 
1957  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // ca claim id present
1958  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1959  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1960  BEAST_EXPECT(
1961  scEnv.claimCount(jvb) == 0); // claim count still 0
1962  }
1963 
1964  {
1965  // complete attestations for 2nd account create => should
1966  // not complete
1967  Balance attester(scEnv, scAttester);
1968  Balance door(scEnv, Account::master);
1969 
1970  scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 3, 2))
1971  .close();
1972 
1973  BEAST_EXPECT(door.diff() == STAmount(0));
1974  // att_create_acct_vec return vectors of size 3, so 3 txns
1975  BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1976 
1977  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1978  BEAST_EXPECT(
1979  scEnv.claimCount(jvb) == 0); // claim count still 0
1980  }
1981 
1982  {
1983  // complete attestations for 3rd account create => should
1984  // not complete
1985  Balance attester(scEnv, scAttester);
1986  Balance door(scEnv, Account::master);
1987 
1988  scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
1989  .close();
1990 
1991  BEAST_EXPECT(door.diff() == STAmount(0));
1992  // att_create_acct_vec return vectors of size 3, so 3 txns
1993  BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1994 
1995  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1996  BEAST_EXPECT(
1997  scEnv.claimCount(jvb) == 0); // claim count still 0
1998  }
1999 
2000  {
2001  // complete attestations for 1st account create => account
2002  // should be created
2003  Balance attester(scEnv, scAttester);
2004  Balance door(scEnv, Account::master);
2005 
2006  scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 3, 1))
2007  .close();
2008 
2009  BEAST_EXPECT(door.diff() == -amt_plus_reward);
2010  // att_create_acct_vec return vectors of size 3, so 3 txns
2011  BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
2012  BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2013 
2014  BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id 1 deleted
2015  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
2016  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2017  BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count now 1
2018  }
2019 
2020  {
2021  // resend attestations for 3rd account create => still
2022  // should not complete
2023  Balance attester(scEnv, scAttester);
2024  Balance door(scEnv, Account::master);
2025 
2026  scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
2027  .close();
2028 
2029  BEAST_EXPECT(door.diff() == STAmount(0));
2030  // att_create_acct_vec return vectors of size 3, so 3 txns
2031  BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
2032 
2033  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
2034  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2035  BEAST_EXPECT(
2036  scEnv.claimCount(jvb) == 1); // claim count still 1
2037  }
2038 
2039  {
2040  // resend attestations for 2nd account create => account
2041  // should be created
2042  Balance attester(scEnv, scAttester);
2043  Balance door(scEnv, Account::master);
2044 
2045  scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1)).close();
2046 
2047  BEAST_EXPECT(door.diff() == -amt_plus_reward);
2048  BEAST_EXPECT(attester.diff() == -tx_fee);
2049  BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2050 
2051  BEAST_EXPECT(!scEnv.caClaimID(jvb, 2)); // claim id 2 deleted
2052  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2053  BEAST_EXPECT(scEnv.claimCount(jvb) == 2); // claim count now 2
2054  }
2055  {
2056  // resend attestations for 3rc account create => account
2057  // should be created
2058  Balance attester(scEnv, scAttester);
2059  Balance door(scEnv, Account::master);
2060 
2061  scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1)).close();
2062 
2063  BEAST_EXPECT(door.diff() == -amt_plus_reward);
2064  BEAST_EXPECT(attester.diff() == -tx_fee);
2065  BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2066 
2067  BEAST_EXPECT(!scEnv.caClaimID(jvb, 3)); // claim id 3 deleted
2068  BEAST_EXPECT(scEnv.claimCount(jvb) == 3); // claim count now 3
2069  }
2070  }
2071 
2072  // Check that creating an account with less than the minimum reserve
2073  // fails.
2074  {
2075  XEnv mcEnv(*this);
2076  XEnv scEnv(*this, true);
2077 
2078  auto const amt = res0 - XRP(1);
2079  auto const amt_plus_reward = amt + reward;
2080 
2081  mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2082 
2083  {
2084  Balance door(mcEnv, mcDoor);
2085  Balance carol(mcEnv, mcCarol);
2086 
2087  mcEnv
2089  mcCarol, jvb, scuAlice, amt, reward))
2090  .close();
2091 
2092  BEAST_EXPECT(door.diff() == amt_plus_reward);
2093  BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2094  }
2095 
2096  scEnv.tx(create_bridge(Account::master, jvb, reward, XRP(20)))
2098  .close();
2099 
2100  Balance attester(scEnv, scAttester);
2101  Balance door(scEnv, Account::master);
2102 
2103  scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)).close();
2104  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2105  BEAST_EXPECT(
2106  scEnv.claimCount(jvb) == 0); // claim count is one less
2107 
2108  scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2, 2)).close();
2109  BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2110  BEAST_EXPECT(
2111  scEnv.claimCount(jvb) == 1); // claim count was incremented
2112 
2113  BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2114  BEAST_EXPECT(door.diff() == -reward);
2115  BEAST_EXPECT(!scEnv.account(scuAlice));
2116  }
2117 
2118  // Check that sending funds with an account create txn to an
2119  // existing account works.
2120  {
2121  XEnv mcEnv(*this);
2122  XEnv scEnv(*this, true);
2123 
2124  auto const amt = XRP(111);
2125  auto const amt_plus_reward = amt + reward;
2126 
2127  mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2128 
2129  {
2130  Balance door(mcEnv, mcDoor);
2131  Balance carol(mcEnv, mcCarol);
2132 
2133  mcEnv
2135  mcCarol, jvb, scAlice, amt, reward))
2136  .close();
2137 
2138  BEAST_EXPECT(door.diff() == amt_plus_reward);
2139  BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2140  }
2141 
2142  scEnv.tx(create_bridge(Account::master, jvb, reward, XRP(20)))
2144  .close();
2145 
2146  Balance attester(scEnv, scAttester);
2147  Balance door(scEnv, Account::master);
2148  Balance alice(scEnv, scAlice);
2149 
2150  scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2151  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2152  BEAST_EXPECT(
2153  scEnv.claimCount(jvb) == 0); // claim count is one less
2154 
2155  scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2156  BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2157  BEAST_EXPECT(
2158  scEnv.claimCount(jvb) == 1); // claim count was incremented
2159 
2160  BEAST_EXPECT(door.diff() == -amt_plus_reward);
2161  BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2162  BEAST_EXPECT(alice.diff() == amt);
2163  }
2164 
2165  // Check that sending funds to an existing account with deposit auth
2166  // set fails for account create transactions.
2167  {
2168  XEnv mcEnv(*this);
2169  XEnv scEnv(*this, true);
2170 
2171  auto const amt = XRP(1000);
2172  auto const amt_plus_reward = amt + reward;
2173 
2174  mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2175 
2176  {
2177  Balance door(mcEnv, mcDoor);
2178  Balance carol(mcEnv, mcCarol);
2179 
2180  mcEnv
2182  mcCarol, jvb, scAlice, amt, reward))
2183  .close();
2184 
2185  BEAST_EXPECT(door.diff() == amt_plus_reward);
2186  BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2187  }
2188 
2189  scEnv.tx(create_bridge(Account::master, jvb, reward, XRP(20)))
2191  .tx(fset("scAlice", asfDepositAuth)) // set deposit auth
2192  .close();
2193 
2194  Balance attester(scEnv, scAttester);
2195  Balance door(scEnv, Account::master);
2196  Balance alice(scEnv, scAlice);
2197 
2198  scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2199  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2200  BEAST_EXPECT(
2201  scEnv.claimCount(jvb) == 0); // claim count is one less
2202 
2203  scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2204  BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2205  BEAST_EXPECT(
2206  scEnv.claimCount(jvb) == 1); // claim count was incremented
2207 
2208  BEAST_EXPECT(door.diff() == -reward);
2209  BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2210  BEAST_EXPECT(alice.diff() == STAmount(0));
2211  }
2212 
2213  // If an account is unable to pay the reserve, check that it fails.
2214  // [greg todo] I don't know what this should test??
2215 
2216  // If an attestation already exists for that server and claim id,
2217  // the new attestation should replace the old attestation
2218  {
2219  XEnv mcEnv(*this);
2220  XEnv scEnv(*this, true);
2221  auto const amt = XRP(1000);
2222  auto const amt_plus_reward = amt + reward;
2223 
2224  {
2225  Balance door(mcEnv, mcDoor);
2226  Balance carol(mcEnv, mcCarol);
2227 
2228  mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
2229  .close()
2231  mcAlice, jvb, scuAlice, amt, reward))
2232  .close() // make sure Alice gets claim #1
2234  mcBob, jvb, scuBob, amt, reward))
2235  .close() // make sure Bob gets claim #2
2237  mcCarol, jvb, scuCarol, amt, reward))
2238  .close(); // and Carol will get claim #3
2239 
2240  BEAST_EXPECT(
2241  door.diff() ==
2242  (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
2243  tx_fee));
2244  BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
2245  }
2246 
2247  std::uint32_t const red_quorum = 2;
2248  scEnv.tx(create_bridge(Account::master, jvb, reward, XRP(20)))
2249  .tx(jtx::signers(Account::master, red_quorum, signers))
2250  .close();
2251 
2252  {
2253  Balance attester(scEnv, scAttester);
2254  Balance door(scEnv, Account::master);
2255  auto const bad_amt = XRP(10);
2256  std::uint32_t txCount = 0;
2257 
2258  // send attestations with incorrect amounts to for all 3
2259  // AccountCreate. They will be replaced later
2260  scEnv.multiTx(att_create_acct_vec(1, bad_amt, scuAlice, 1))
2261  .multiTx(att_create_acct_vec(2, bad_amt, scuBob, 1, 2))
2262  .multiTx(att_create_acct_vec(3, bad_amt, scuCarol, 1, 1))
2263  .close();
2264  txCount += 3;
2265 
2266  BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 created");
2267  BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 created");
2268  BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 created");
2269 
2270  // note: if we send inconsistent attestations in the same
2271  // batch, the transaction errors.
2272 
2273  // from now on we send correct attestations
2274  scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 0))
2275  .multiTx(att_create_acct_vec(2, amt, scuBob, 1, 2))
2276  .multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 4))
2277  .close();
2278  txCount += 3;
2279 
2280  BEAST_EXPECTS(
2281  !!scEnv.caClaimID(jvb, 1), "claim id 1 still there");
2282  BEAST_EXPECTS(
2283  !!scEnv.caClaimID(jvb, 2), "claim id 2 still there");
2284  BEAST_EXPECTS(
2285  !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2286  BEAST_EXPECTS(
2287  scEnv.claimCount(jvb) == 0, "No account created yet");
2288 
2289  scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 1))
2290  .close();
2291  txCount += 1;
2292 
2293  BEAST_EXPECTS(
2294  !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2295  BEAST_EXPECTS(
2296  scEnv.claimCount(jvb) == 0, "No account created yet");
2297 
2298  scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 2))
2299  .close();
2300  txCount += 1;
2301 
2302  BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 deleted");
2303  BEAST_EXPECTS(scEnv.claimCount(jvb) == 1, "scuAlice created");
2304 
2305  scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1, 3))
2306  .multiTx(
2307  att_create_acct_vec(1, amt, scuAlice, 1, 3),
2309  .close();
2310  txCount += 2;
2311 
2312  BEAST_EXPECTS(!scEnv.caClaimID(jvb, 2), "claim id 2 deleted");
2313  BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 not added");
2314  BEAST_EXPECTS(
2315  scEnv.claimCount(jvb) == 2, "scuAlice & scuBob created");
2316 
2317  scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 0))
2318  .close();
2319  txCount += 1;
2320 
2321  BEAST_EXPECTS(!scEnv.caClaimID(jvb, 3), "claim id 3 deleted");
2322  BEAST_EXPECTS(
2323  scEnv.claimCount(jvb) == 3, "All 3 accounts created");
2324 
2325  // because of the division of the rewards among attesters,
2326  // sometimes a couple drops are left over unspent in the
2327  // door account (here 2 drops)
2328  BEAST_EXPECT(
2329  multiply(amt_plus_reward, STAmount(3), xrpIssue()) +
2330  door.diff() <
2331  drops(3));
2332  BEAST_EXPECT(attester.diff() == -multiTtxFee(txCount));
2333  BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2334  BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2335  BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2336  }
2337  }
2338 
2339  // If attestation moves funds, confirm the claim ledger objects are
2340  // removed (for both account create and "regular" transactions)
2341  // [greg] we do this in all attestation tests
2342 
2343  // coverage test: add_attestation transaction with incorrect flag
2344  {
2345  XEnv scEnv(*this, true);
2348  .close()
2350  scAttester,
2351  jvb,
2352  mcAlice,
2353  XRP(1000),
2354  payees[0],
2355  true,
2356  1,
2357  {},
2358  signers[0]),
2361  .close();
2362  }
2363 
2364  // coverage test: add_attestation with xchain feature
2365  // disabled
2366  {
2367  XEnv scEnv(*this, true);
2371  .close()
2373  scAttester,
2374  jvb,
2375  mcAlice,
2376  XRP(1000),
2377  payees[0],
2378  true,
2379  1,
2380  {},
2381  signers[0]),
2382  ter(temDISABLED))
2383  .close();
2384  }
2385  }
2386 
2387  void
2389  {
2390  using namespace jtx;
2391 
2392  testcase("Add Non Batch Claim Attestation");
2393 
2394  {
2395  XEnv mcEnv(*this);
2396  XEnv scEnv(*this, true);
2397  std::uint32_t const claimID = 1;
2398 
2399  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2400 
2403  .close()
2405  .close();
2406 
2407  BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
2408 
2409  Account const dst{scBob};
2410  auto const amt = XRP(1000);
2411  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2412 
2413  auto const dstStartBalance = scEnv.env_.balance(dst);
2414 
2415  for (int i = 0; i < signers.size(); ++i)
2416  {
2417  auto const att = claim_attestation(
2418  scAttester,
2419  jvb,
2420  mcAlice,
2421  amt,
2422  payees[i],
2423  true,
2424  claimID,
2425  dst,
2426  signers[i]);
2427 
2428  TER const expectedTER =
2430  if (i + 1 == quorum)
2431  scEnv.tx(att, ter(expectedTER)).close();
2432  else
2433  scEnv.tx(att, ter(expectedTER)).close();
2434 
2435  if (i + 1 < quorum)
2436  BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2437  else
2438  BEAST_EXPECT(
2439  dstStartBalance + amt == scEnv.env_.balance(dst));
2440  }
2441  BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2442  }
2443 
2444  {
2461  XEnv mcEnv(*this);
2462  XEnv scEnv(*this, true);
2463  auto const amt = XRP(1000);
2464  std::uint32_t const claimID = 1;
2465 
2466  for (auto i = 0; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2; ++i)
2467  scEnv.fund(amt, alt_signers[i].account);
2468 
2469  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2470 
2473  .close()
2475  .close();
2476 
2477  Account const dst{scBob};
2478  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2479  auto const dstStartBalance = scEnv.env_.balance(dst);
2480 
2481  {
2482  // G1: master key
2483  auto att = claim_attestation(
2484  scAttester,
2485  jvb,
2486  mcAlice,
2487  amt,
2488  payees[0],
2489  true,
2490  claimID,
2491  dst,
2492  alt_signers[0]);
2493  scEnv.tx(att).close();
2494  }
2495  {
2496  // G2: regular key
2497  // alt_signers[0] is the regular key of alt_signers[1]
2498  // There should be 2 attestations after the transaction
2499  scEnv
2500  .tx(jtx::regkey(
2502  .close();
2503  auto att = claim_attestation(
2504  scAttester,
2505  jvb,
2506  mcAlice,
2507  amt,
2508  payees[1],
2509  true,
2510  claimID,
2511  dst,
2512  alt_signers[0]);
2514  alt_signers[1].account.human();
2515  scEnv.tx(att).close();
2516  }
2517  {
2518  // B3: public key and non-exist (unfunded) account mismatch
2519  // G3: public key and non-exist (unfunded) account match
2520  auto const unfundedSigner1 =
2522  auto const unfundedSigner2 =
2524  auto att = claim_attestation(
2525  scAttester,
2526  jvb,
2527  mcAlice,
2528  amt,
2530  true,
2531  claimID,
2532  dst,
2533  unfundedSigner1);
2535  unfundedSigner2.account.human();
2537  .close();
2539  unfundedSigner1.account.human();
2540  scEnv.tx(att).close();
2541  }
2542  {
2543  // B2: single item signer list
2544  std::vector<signer> tempSignerList = {signers[0]};
2545  scEnv.tx(
2546  jtx::signers(alt_signers[2].account, 1, tempSignerList));
2547  auto att = claim_attestation(
2548  scAttester,
2549  jvb,
2550  mcAlice,
2551  amt,
2552  payees[2],
2553  true,
2554  claimID,
2555  dst,
2556  tempSignerList.front());
2558  alt_signers[2].account.human();
2560  .close();
2561  }
2562  {
2563  // B1: disabled master key
2564  scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0));
2565  auto att = claim_attestation(
2566  scAttester,
2567  jvb,
2568  mcAlice,
2569  amt,
2570  payees[2],
2571  true,
2572  claimID,
2573  dst,
2574  alt_signers[2]);
2576  .close();
2577  }
2578  {
2579  // --B4: not on signer list
2580  auto att = claim_attestation(
2581  scAttester,
2582  jvb,
2583  mcAlice,
2584  amt,
2585  payees[0],
2586  true,
2587  claimID,
2588  dst,
2589  signers[0]);
2590  scEnv.tx(att, ter(tecNO_PERMISSION)).close();
2591  }
2592  {
2593  // --B5: missing sfAttestationSignerAccount field
2594  // Then submit the one with the field. Should rearch quorum.
2595  auto att = claim_attestation(
2596  scAttester,
2597  jvb,
2598  mcAlice,
2599  amt,
2600  payees[3],
2601  true,
2602  claimID,
2603  dst,
2604  alt_signers[3]);
2605  att.removeMember(sfAttestationSignerAccount.getJsonName());
2606  scEnv.tx(att, ter(temMALFORMED)).close();
2607  BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2609  alt_signers[3].account.human();
2610  scEnv.tx(att).close();
2611  BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2612  }
2613  }
2614  }
2615 
2616  void
2618  {
2619  using namespace jtx;
2620 
2621  testcase("Add Non Batch Account Create Attestation");
2622 
2623  XEnv mcEnv(*this);
2624  XEnv scEnv(*this, true);
2625 
2626  XRPAmount tx_fee = mcEnv.txFee();
2627 
2628  Account a{"a"};
2629  Account doorA{"doorA"};
2630 
2631  STAmount funds{XRP(10000)};
2632  mcEnv.fund(funds, a);
2633  mcEnv.fund(funds, doorA);
2634 
2635  Account ua{"ua"}; // unfunded account we want to create
2636 
2637  BridgeDef xrp_b{
2638  doorA,
2639  xrpIssue(),
2641  xrpIssue(),
2642  XRP(1), // reward
2643  XRP(20), // minAccountCreate
2644  4, // quorum
2645  signers,
2646  Json::nullValue};
2647 
2648  xrp_b.initBridge(mcEnv, scEnv);
2649 
2650  auto const amt = XRP(777);
2651  auto const amt_plus_reward = amt + xrp_b.reward;
2652  {
2653  Balance bal_doorA(mcEnv, doorA);
2654  Balance bal_a(mcEnv, a);
2655 
2656  mcEnv
2658  a, xrp_b.jvb, ua, amt, xrp_b.reward))
2659  .close();
2660 
2661  BEAST_EXPECT(bal_doorA.diff() == amt_plus_reward);
2662  BEAST_EXPECT(bal_a.diff() == -(amt_plus_reward + tx_fee));
2663  }
2664 
2665  for (int i = 0; i < signers.size(); ++i)
2666  {
2667  auto const att = create_account_attestation(
2668  signers[0].account,
2669  xrp_b.jvb,
2670  a,
2671  amt,
2672  xrp_b.reward,
2673  signers[i].account,
2674  true,
2675  1,
2676  ua,
2677  signers[i]);
2678  TER const expectedTER = i < xrp_b.quorum
2679  ? tesSUCCESS
2681 
2682  scEnv.tx(att, ter(expectedTER)).close();
2683  if (i + 1 < xrp_b.quorum)
2684  BEAST_EXPECT(!scEnv.env_.le(ua));
2685  else
2686  BEAST_EXPECT(scEnv.env_.le(ua));
2687  }
2688  BEAST_EXPECT(scEnv.env_.le(ua));
2689  }
2690 
2691  void
2693  {
2694  using namespace jtx;
2695 
2696  XRPAmount res0 = reserve(0);
2697  XRPAmount tx_fee = txFee();
2698 
2699  testcase("Claim");
2700 
2701  // Claim where the amount matches what is attested to, to an account
2702  // that exists, and there are enough attestations to reach a quorum
2703  // => should succeed
2704  // -----------------------------------------------------------------
2705  for (auto withClaim : {false, true})
2706  {
2707  XEnv mcEnv(*this);
2708  XEnv scEnv(*this, true);
2709 
2710  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2711 
2714  .close()
2716  .close();
2717 
2718  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2719  auto const amt = XRP(1000);
2720  std::uint32_t const claimID = 1;
2721  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2722 
2723  BalanceTransfer transfer(
2724  scEnv,
2726  scBob,
2727  scAlice,
2728  &payees[0],
2730  withClaim);
2731 
2732  scEnv
2734  scAttester,
2735  jvb,
2736  mcAlice,
2737  amt,
2738  payees,
2739  true,
2740  claimID,
2741  dst,
2742  signers))
2743  .close();
2744  if (withClaim)
2745  {
2746  BEAST_EXPECT(transfer.has_not_happened());
2747 
2748  // need to submit a claim transactions
2749  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
2750  .close();
2751  }
2752 
2753  BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2754  }
2755 
2756  // Claim with just one attestation signed by the Master key
2757  // => should not succeed
2758  // -----------------------------------------------------------------
2759  for (auto withClaim : {false, true})
2760  {
2761  XEnv mcEnv(*this);
2762  XEnv scEnv(*this, true);
2763 
2764  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2765 
2766  scEnv
2768  //.tx(jtx::signers(Account::master, quorum, signers))
2769  .close()
2771  .close();
2772 
2773  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2774  auto const amt = XRP(1000);
2775  std::uint32_t const claimID = 1;
2776  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2777 
2778  BalanceTransfer transfer(
2779  scEnv,
2781  scBob,
2782  scAlice,
2783  &payees[0],
2784  1,
2785  withClaim);
2786 
2787  jtx::signer master_signer(Account::master);
2788  scEnv
2790  scAttester,
2791  jvb,
2792  mcAlice,
2793  amt,
2794  payees[0],
2795  true,
2796  claimID,
2797  dst,
2798  master_signer),
2800  .close();
2801 
2802  BEAST_EXPECT(transfer.has_not_happened());
2803  }
2804 
2805  // Claim with just one attestation signed by a regular key
2806  // associated to the master account
2807  // => should not succeed
2808  // -----------------------------------------------------------------
2809  for (auto withClaim : {false, true})
2810  {
2811  XEnv mcEnv(*this);
2812  XEnv scEnv(*this, true);
2813 
2814  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2815 
2816  scEnv
2818  //.tx(jtx::signers(Account::master, quorum, signers))
2820  .close()
2822  .close();
2823 
2824  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2825  auto const amt = XRP(1000);
2826  std::uint32_t const claimID = 1;
2827  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2828 
2829  BalanceTransfer transfer(
2830  scEnv,
2832  scBob,
2833  scAlice,
2834  &payees[0],
2835  1,
2836  withClaim);
2837 
2838  jtx::signer master_signer(payees[0]);
2839  scEnv
2841  scAttester,
2842  jvb,
2843  mcAlice,
2844  amt,
2845  payees[0],
2846  true,
2847  claimID,
2848  dst,
2849  master_signer),
2851  .close();
2852 
2853  BEAST_EXPECT(transfer.has_not_happened());
2854  }
2855 
2856  // Claim against non-existent bridge
2857  // ---------------------------------
2858  for (auto withClaim : {false, true})
2859  {
2860  XEnv mcEnv(*this);
2861  XEnv scEnv(*this, true);
2862 
2863  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2864 
2865  auto jvb_unknown =
2867 
2870  .close()
2872  scAlice, jvb_unknown, reward, mcAlice),
2873  ter(tecNO_ENTRY))
2874  .close();
2875 
2876  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2877  auto const amt = XRP(1000);
2878  std::uint32_t const claimID = 1;
2879  mcEnv
2880  .tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst),
2881  ter(tecNO_ENTRY))
2882  .close();
2883 
2884  BalanceTransfer transfer(
2885  scEnv, Account::master, scBob, scAlice, payees, withClaim);
2886  scEnv
2888  scAttester,
2889  jvb_unknown,
2890  mcAlice,
2891  amt,
2892  payees[0],
2893  true,
2894  claimID,
2895  dst,
2896  signers[0]),
2897  ter(tecNO_ENTRY))
2898  .close();
2899 
2900  if (withClaim)
2901  {
2902  BEAST_EXPECT(transfer.has_not_happened());
2903 
2904  // need to submit a claim transactions
2905  scEnv
2906  .tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob),
2907  ter(tecNO_ENTRY))
2908  .close();
2909  }
2910 
2911  BEAST_EXPECT(transfer.has_not_happened());
2912  }
2913 
2914  // Claim against non-existent claim id
2915  // -----------------------------------
2916  for (auto withClaim : {false, true})
2917  {
2918  XEnv mcEnv(*this);
2919  XEnv scEnv(*this, true);
2920 
2921  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2922 
2925  .close()
2927  .close();
2928 
2929  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2930  auto const amt = XRP(1000);
2931  std::uint32_t const claimID = 1;
2932  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2933 
2934  BalanceTransfer transfer(
2935  scEnv, Account::master, scBob, scAlice, payees, withClaim);
2936 
2937  // attest using non-existent claim id
2938  scEnv
2940  scAttester,
2941  jvb,
2942  mcAlice,
2943  amt,
2944  payees[0],
2945  true,
2946  999,
2947  dst,
2948  signers[0]),
2950  .close();
2951  if (withClaim)
2952  {
2953  BEAST_EXPECT(transfer.has_not_happened());
2954 
2955  // claim using non-existent claim id
2956  scEnv
2957  .tx(xchain_claim(scAlice, jvb, 999, amt, scBob),
2959  .close();
2960  }
2961 
2962  BEAST_EXPECT(transfer.has_not_happened());
2963  }
2964 
2965  // Claim against a claim id owned by another account
2966  // -------------------------------------------------
2967  for (auto withClaim : {false, true})
2968  {
2969  XEnv mcEnv(*this);
2970  XEnv scEnv(*this, true);
2971 
2972  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2973 
2976  .close()
2978  .close();
2979 
2980  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2981  auto const amt = XRP(1000);
2982  std::uint32_t const claimID = 1;
2983  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2984 
2985  BalanceTransfer transfer(
2986  scEnv,
2988  scBob,
2989  scAlice,
2990  &payees[0],
2992  withClaim);
2993 
2994  scEnv
2996  scAttester,
2997  jvb,
2998  mcAlice,
2999  amt,
3000  payees,
3001  true,
3002  claimID,
3003  dst,
3004  signers))
3005  .close();
3006  if (withClaim)
3007  {
3008  BEAST_EXPECT(transfer.has_not_happened());
3009 
3010  // submit a claim transaction with the wrong account (scGw
3011  // instead of scAlice)
3012  scEnv
3013  .tx(xchain_claim(scGw, jvb, claimID, amt, scBob),
3015  .close();
3016  BEAST_EXPECT(transfer.has_not_happened());
3017  }
3018  else
3019  {
3020  BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3021  }
3022  }
3023 
3024  // Claim against a claim id with no attestations
3025  // ---------------------------------------------
3026  for (auto withClaim : {false, true})
3027  {
3028  XEnv mcEnv(*this);
3029  XEnv scEnv(*this, true);
3030 
3031  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3032 
3035  .close()
3037  .close();
3038 
3039  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3040  auto const amt = XRP(1000);
3041  std::uint32_t const claimID = 1;
3042  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3043 
3044  BalanceTransfer transfer(
3045  scEnv, Account::master, scBob, scAlice, payees, withClaim);
3046 
3047  // don't send any attestations
3048 
3049  if (withClaim)
3050  {
3051  BEAST_EXPECT(transfer.has_not_happened());
3052 
3053  // need to submit a claim transactions
3054  scEnv
3055  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3057  .close();
3058  }
3059 
3060  BEAST_EXPECT(transfer.has_not_happened());
3061  }
3062 
3063  // Claim against a claim id with attestations, but not enough to
3064  // make a quorum
3065  // --------------------------------------------------------------------
3066  for (auto withClaim : {false, true})
3067  {
3068  XEnv mcEnv(*this);
3069  XEnv scEnv(*this, true);
3070 
3071  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3072 
3075  .close()
3077  .close();
3078 
3079  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3080  auto const amt = XRP(1000);
3081  std::uint32_t const claimID = 1;
3082  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3083 
3084  BalanceTransfer transfer(
3085  scEnv, Account::master, scBob, scAlice, payees, withClaim);
3086 
3087  auto tooFew = quorum - 1;
3088  scEnv
3090  scAttester,
3091  jvb,
3092  mcAlice,
3093  amt,
3094  payees,
3095  true,
3096  claimID,
3097  dst,
3098  signers,
3099  tooFew))
3100  .close();
3101  if (withClaim)
3102  {
3103  BEAST_EXPECT(transfer.has_not_happened());
3104 
3105  // need to submit a claim transactions
3106  scEnv
3107  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3109  .close();
3110  }
3111 
3112  BEAST_EXPECT(transfer.has_not_happened());
3113  }
3114 
3115  // Claim id of zero
3116  // ----------------
3117  for (auto withClaim : {false, true})
3118  {
3119  XEnv mcEnv(*this);
3120  XEnv scEnv(*this, true);
3121 
3122  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3123 
3126  .close()
3128  .close();
3129 
3130  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3131  auto const amt = XRP(1000);
3132  std::uint32_t const claimID = 1;
3133  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3134 
3135  BalanceTransfer transfer(
3136  scEnv, Account::master, scBob, scAlice, payees, withClaim);
3137 
3138  scEnv
3139  .multiTx(
3141  scAttester,
3142  jvb,
3143  mcAlice,
3144  amt,
3145  payees,
3146  true,
3147  0,
3148  dst,
3149  signers),
3151  .close();
3152  if (withClaim)
3153  {
3154  BEAST_EXPECT(transfer.has_not_happened());
3155 
3156  // need to submit a claim transactions
3157  scEnv
3158  .tx(xchain_claim(scAlice, jvb, 0, amt, scBob),
3160  .close();
3161  }
3162 
3163  BEAST_EXPECT(transfer.has_not_happened());
3164  }
3165 
3166  // Claim issue that does not match the expected issue on the bridge
3167  // (either LockingChainIssue or IssuingChainIssue, depending on the
3168  // chain). The claim id should already have enough attestations to
3169  // reach a quorum for this amount (for a different issuer).
3170  // ---------------------------------------------------------------------
3171  for (auto withClaim : {true})
3172  {
3173  XEnv mcEnv(*this);
3174  XEnv scEnv(*this, true);
3175 
3176  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3177 
3180  .close()
3182  .close();
3183 
3184  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3185  auto const amt = XRP(1000);
3186  std::uint32_t const claimID = 1;
3187  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3188 
3189  BalanceTransfer transfer(
3190  scEnv,
3192  scBob,
3193  scAlice,
3194  &payees[0],
3196  withClaim);
3197 
3198  scEnv
3200  scAttester,
3201  jvb,
3202  mcAlice,
3203  amt,
3204  payees,
3205  true,
3206  claimID,
3207  dst,
3208  signers))
3209  .close();
3210 
3211  if (withClaim)
3212  {
3213  BEAST_EXPECT(transfer.has_not_happened());
3214 
3215  // need to submit a claim transactions
3216  scEnv
3218  ter(temBAD_AMOUNT))
3219  .close();
3220  }
3221 
3222  BEAST_EXPECT(transfer.has_not_happened());
3223  }
3224 
3225  // Claim to a destination that does not already exist on the chain
3226  // -----------------------------------------------------------------
3227  for (auto withClaim : {true})
3228  {
3229  XEnv mcEnv(*this);
3230  XEnv scEnv(*this, true);
3231 
3232  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3233 
3236  .close()
3238  .close();
3239 
3240  auto dst(withClaim ? std::nullopt : std::optional<Account>{scuBob});
3241  auto const amt = XRP(1000);
3242  std::uint32_t const claimID = 1;
3243  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3244 
3245  BalanceTransfer transfer(
3246  scEnv,
3248  scBob,
3249  scAlice,
3250  &payees[0],
3252  withClaim);
3253 
3254  scEnv
3256  scAttester,
3257  jvb,
3258  mcAlice,
3259  amt,
3260  payees,
3261  true,
3262  claimID,
3263  dst,
3264  signers))
3265  .close();
3266  if (withClaim)
3267  {
3268  BEAST_EXPECT(transfer.has_not_happened());
3269 
3270  // need to submit a claim transactions
3271  scEnv
3273  ter(tecNO_DST))
3274  .close();
3275  }
3276 
3277  BEAST_EXPECT(transfer.has_not_happened());
3278  }
3279 
3280  // Claim where the claim id owner does not have enough XRP to pay
3281  // the reward
3282  // ------------------------------------------------------------------
3283  for (auto withClaim : {false, true})
3284  {
3285  XEnv mcEnv(*this);
3286  XEnv scEnv(*this, true);
3287 
3288  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3289  STAmount huge_reward{XRP(20000)};
3290  BEAST_EXPECT(huge_reward > scEnv.balance(scAlice));
3291 
3292  scEnv.tx(create_bridge(Account::master, jvb, huge_reward))
3294  .close()
3295  .tx(xchain_create_claim_id(scAlice, jvb, huge_reward, mcAlice))
3296  .close();
3297 
3298  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3299  auto const amt = XRP(1000);
3300  std::uint32_t const claimID = 1;
3301  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3302 
3303  BalanceTransfer transfer(
3304  scEnv,
3306  scBob,
3307  scAlice,
3308  &payees[0],
3310  withClaim);
3311 
3312  if (withClaim)
3313  {
3314  scEnv
3316  scAttester,
3317  jvb,
3318  mcAlice,
3319  amt,
3320  payees,
3321  true,
3322  claimID,
3323  dst,
3324  signers))
3325  .close();
3326  BEAST_EXPECT(transfer.has_not_happened());
3327 
3328  // need to submit a claim transactions
3329  scEnv
3330  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3332  .close();
3333  }
3334  else
3335  {
3336  auto txns = claim_attestations(
3337  scAttester,
3338  jvb,
3339  mcAlice,
3340  amt,
3341  payees,
3342  true,
3343  claimID,
3344  dst,
3345  signers);
3346  for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3347  {
3348  scEnv.tx(txns[i]).close();
3349  }
3350  scEnv.tx(txns.back());
3351  scEnv.close();
3352  // The attestation should succeed, because it adds an
3353  // attestation, but the claim should fail with insufficient
3354  // funds
3355  scEnv
3356  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3358  .close();
3359  }
3360 
3361  BEAST_EXPECT(transfer.has_not_happened());
3362  }
3363 
3364  // Claim where the claim id owner has enough XRP to pay the reward,
3365  // but it would put his balance below the reserve
3366  // --------------------------------------------------------------------
3367  for (auto withClaim : {false, true})
3368  {
3369  XEnv mcEnv(*this);
3370  XEnv scEnv(*this, true);
3371 
3372  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3373 
3376  .fund(
3377  res0 + reward,
3378  scuAlice) // just not enough because of fees
3379  .close()
3382  .close();
3383 
3384  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3385  auto const amt = XRP(1000);
3386  std::uint32_t const claimID = 1;
3387  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3388 
3389  BalanceTransfer transfer(
3390  scEnv, Account::master, scBob, scuAlice, payees, withClaim);
3391 
3392  scEnv
3394  scAttester,
3395  jvb,
3396  mcAlice,
3397  amt,
3398  payees[0],
3399  true,
3400  claimID,
3401  dst,
3402  signers[0]),
3404  .close();
3405  if (withClaim)
3406  {
3407  BEAST_EXPECT(transfer.has_not_happened());
3408 
3409  // need to submit a claim transactions
3410  scEnv
3413  .close();
3414  }
3415 
3416  BEAST_EXPECT(transfer.has_not_happened());
3417  }
3418 
3419  // Pay to an account with deposit auth set
3420  // ---------------------------------------
3421  for (auto withClaim : {false, true})
3422  {
3423  XEnv mcEnv(*this);
3424  XEnv scEnv(*this, true);
3425 
3426  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3427 
3430  .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3431  .close()
3433  .close();
3434 
3435  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3436  auto const amt = XRP(1000);
3437  std::uint32_t const claimID = 1;
3438  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3439 
3440  BalanceTransfer transfer(
3441  scEnv,
3443  scBob,
3444  scAlice,
3445  &payees[0],
3447  withClaim);
3448  auto txns = claim_attestations(
3449  scAttester,
3450  jvb,
3451  mcAlice,
3452  amt,
3453  payees,
3454  true,
3455  claimID,
3456  dst,
3457  signers);
3458  for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3459  {
3460  scEnv.tx(txns[i]).close();
3461  }
3462  if (withClaim)
3463  {
3464  scEnv.tx(txns.back()).close();
3465 
3466  BEAST_EXPECT(transfer.has_not_happened());
3467 
3468  // need to submit a claim transactions
3469  scEnv
3470  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3472  .close();
3473 
3474  // the transfer failed, but check that we can still use the
3475  // claimID with a different account
3476  Balance scCarol_bal(scEnv, scCarol);
3477 
3478  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3479  .close();
3480  BEAST_EXPECT(scCarol_bal.diff() == amt);
3481  }
3482  else
3483  {
3484  scEnv.tx(txns.back()).close();
3485  scEnv
3486  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3488  .close();
3489  // A way would be to remove deposit auth and resubmit the
3490  // attestations (even though the witness servers won't do
3491  // it)
3492  scEnv
3493  .tx(fset("scBob", 0, asfDepositAuth)) // clear deposit auth
3494  .close();
3495 
3496  Balance scBob_bal(scEnv, scBob);
3497  scEnv.tx(txns.back()).close();
3498  BEAST_EXPECT(scBob_bal.diff() == amt);
3499  }
3500  }
3501 
3502  // Pay to an account with Destination Tag set
3503  // ------------------------------------------
3504  for (auto withClaim : {false, true})
3505  {
3506  XEnv mcEnv(*this);
3507  XEnv scEnv(*this, true);
3508 
3509  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3510 
3513  .tx(fset("scBob", asfRequireDest)) // set dest tag
3514  .close()
3516  .close();
3517 
3518  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3519  auto const amt = XRP(1000);
3520  std::uint32_t const claimID = 1;
3521  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3522 
3523  BalanceTransfer transfer(
3524  scEnv,
3526  scBob,
3527  scAlice,
3528  &payees[0],
3530  withClaim);
3531  auto txns = claim_attestations(
3532  scAttester,
3533  jvb,
3534  mcAlice,
3535  amt,
3536  payees,
3537  true,
3538  claimID,
3539  dst,
3540  signers);
3541  for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3542  {
3543  scEnv.tx(txns[i]).close();
3544  }
3545  if (withClaim)
3546  {
3547  scEnv.tx(txns.back()).close();
3548  BEAST_EXPECT(transfer.has_not_happened());
3549 
3550  // need to submit a claim transactions
3551  scEnv
3552  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3554  .close();
3555 
3556  // the transfer failed, but check that we can still use the
3557  // claimID with a different account
3558  Balance scCarol_bal(scEnv, scCarol);
3559 
3560  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3561  .close();
3562  BEAST_EXPECT(scCarol_bal.diff() == amt);
3563  }
3564  else
3565  {
3566  scEnv.tx(txns.back()).close();
3567  scEnv
3568  .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3570  .close();
3571  // A way would be to remove the destination tag requirement
3572  // and resubmit the attestations (even though the witness
3573  // servers won't do it)
3574  scEnv
3575  .tx(fset("scBob", 0, asfRequireDest)) // clear dest tag
3576  .close();
3577 
3578  Balance scBob_bal(scEnv, scBob);
3579 
3580  scEnv.tx(txns.back()).close();
3581  BEAST_EXPECT(scBob_bal.diff() == amt);
3582  }
3583  }
3584 
3585  // Pay to an account with deposit auth set. Check that the attestations
3586  // are still validated and that we can used the claimID to transfer the
3587  // funds to a different account (which doesn't have deposit auth set)
3588  // --------------------------------------------------------------------
3589  {
3590  XEnv mcEnv(*this);
3591  XEnv scEnv(*this, true);
3592 
3593  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3594 
3597  .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3598  .close()
3600  .close();
3601 
3602  auto dst(std::optional<Account>{scBob});
3603  auto const amt = XRP(1000);
3604  std::uint32_t const claimID = 1;
3605  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3606 
3607  // we should be able to submit the attestations, but the transfer
3608  // should not occur because dest account has deposit auth set
3609  Balance scBob_bal(scEnv, scBob);
3610 
3612  scAttester,
3613  jvb,
3614  mcAlice,
3615  amt,
3616  payees,
3617  true,
3618  claimID,
3619  dst,
3620  signers));
3621  BEAST_EXPECT(scBob_bal.diff() == STAmount(0));
3622 
3623  // Check that check that we still can use the claimID to transfer
3624  // the amount to a different account
3625  Balance scCarol_bal(scEnv, scCarol);
3626 
3627  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
3628  BEAST_EXPECT(scCarol_bal.diff() == amt);
3629  }
3630 
3631  // Claim where the amount different from what is attested to
3632  // ---------------------------------------------------------
3633  for (auto withClaim : {true})
3634  {
3635  XEnv mcEnv(*this);
3636  XEnv scEnv(*this, true);
3637 
3638  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3639 
3642  .close()
3644  .close();
3645 
3646  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3647  auto const amt = XRP(1000);
3648  std::uint32_t const claimID = 1;
3649  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3650 
3651  BalanceTransfer transfer(
3652  scEnv,
3654  scBob,
3655  scAlice,
3656  &payees[0],
3658  withClaim);
3660  scAttester,
3661  jvb,
3662  mcAlice,
3663  amt,
3664  payees,
3665  true,
3666  claimID,
3667  dst,
3668  signers));
3669  if (withClaim)
3670  {
3671  BEAST_EXPECT(transfer.has_not_happened());
3672 
3673  // claim wrong amount
3674  scEnv
3677  .close();
3678  }
3679 
3680  BEAST_EXPECT(transfer.has_not_happened());
3681  }
3682 
3683  // Verify that rewards are paid from the account that owns the claim
3684  // id
3685  // --------------------------------------------------------------------
3686  for (auto withClaim : {false, true})
3687  {
3688  XEnv mcEnv(*this);
3689  XEnv scEnv(*this, true);
3690 
3691  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3692 
3695  .close()
3697  .close();
3698 
3699  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3700  auto const amt = XRP(1000);
3701  std::uint32_t const claimID = 1;
3702  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3703 
3704  BalanceTransfer transfer(
3705  scEnv,
3707  scBob,
3708  scAlice,
3709  &payees[0],
3711  withClaim);
3712  Balance scAlice_bal(scEnv, scAlice);
3714  scAttester,
3715  jvb,
3716  mcAlice,
3717  amt,
3718  payees,
3719  true,
3720  claimID,
3721  dst,
3722  signers));
3723 
3724  STAmount claim_cost = reward;
3725 
3726  if (withClaim)
3727  {
3728  BEAST_EXPECT(transfer.has_not_happened());
3729 
3730  // need to submit a claim transactions
3731  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3732  .close();
3733  claim_cost += tx_fee;
3734  }
3735 
3736  BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3737  BEAST_EXPECT(
3738  scAlice_bal.diff() == -claim_cost); // because reward % 4 == 0
3739  }
3740 
3741  // Verify that if a reward is not evenly divisible among the reward
3742  // accounts, the remaining amount goes to the claim id owner.
3743  // ----------------------------------------------------------------
3744  for (auto withClaim : {false, true})
3745  {
3746  XEnv mcEnv(*this);
3747  XEnv scEnv(*this, true);
3748 
3750 
3753  .close()
3755  .close();
3756 
3757  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3758  auto const amt = XRP(1000);
3759  std::uint32_t const claimID = 1;
3760  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3761 
3762  BalanceTransfer transfer(
3763  scEnv,
3765  scBob,
3766  scAlice,
3767  &payees[0],
3769  withClaim);
3770  Balance scAlice_bal(scEnv, scAlice);
3772  scAttester,
3773  jvb,
3774  mcAlice,
3775  amt,
3776  payees,
3777  true,
3778  claimID,
3779  dst,
3780  signers));
3781  STAmount claim_cost = tiny_reward;
3782 
3783  if (withClaim)
3784  {
3785  BEAST_EXPECT(transfer.has_not_happened());
3786 
3787  // need to submit a claim transactions
3788  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3789  .close();
3790  claim_cost += tx_fee;
3791  }
3792 
3793  BEAST_EXPECT(transfer.has_happened(amt, tiny_reward_split));
3794  BEAST_EXPECT(
3795  scAlice_bal.diff() == -(claim_cost - tiny_reward_remainder));
3796  }
3797 
3798  // If a reward distribution fails for one of the reward accounts
3799  // (the reward account doesn't exist or has deposit auth set), then
3800  // the txn should still succeed, but that portion should go to the
3801  // claim id owner.
3802  // -------------------------------------------------------------------
3803  for (auto withClaim : {false, true})
3804  {
3805  XEnv mcEnv(*this);
3806  XEnv scEnv(*this, true);
3807 
3808  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3809 
3810  std::vector<Account> alt_payees{payees.begin(), payees.end() - 1};
3811  alt_payees.back() = Account("inexistent");
3812 
3815  .close()
3817  .close();
3818 
3819  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3820  auto const amt = XRP(1000);
3821  std::uint32_t const claimID = 1;
3822  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3823 
3824  BalanceTransfer transfer(
3825  scEnv,
3827  scBob,
3828  scAlice,
3829  &payees[0],
3831  withClaim);
3833  scAttester,
3834  jvb,
3835  mcAlice,
3836  amt,
3837  alt_payees,
3838  true,
3839  claimID,
3840  dst,
3841  signers));
3842 
3843  if (withClaim)
3844  {
3845  BEAST_EXPECT(transfer.has_not_happened());
3846 
3847  // need to submit a claim transactions
3848  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3849  .close();
3850  }
3851 
3852  // this also checks that only 3 * split_reward was deducted from
3853  // scAlice (the payor account), since we passed alt_payees to
3854  // BalanceTransfer
3855  BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3856  }
3857 
3858  for (auto withClaim : {false, true})
3859  {
3860  XEnv mcEnv(*this);
3861  XEnv scEnv(*this, true);
3862 
3863  mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3864  auto& unpaid = payees[UT_XCHAIN_DEFAULT_QUORUM - 1];
3867  .tx(fset(unpaid, asfDepositAuth))
3868  .close()
3870  .close();
3871 
3872  auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3873  auto const amt = XRP(1000);
3874  std::uint32_t const claimID = 1;
3875  mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3876 
3877  // balance of last signer should not change (has deposit auth)
3878  Balance last_signer(scEnv, unpaid);
3879 
3880  // make sure all signers except the last one get the
3881  // split_reward
3882 
3883  BalanceTransfer transfer(
3884  scEnv,
3886  scBob,
3887  scAlice,
3888  &payees[0],
3890  withClaim);
3892  scAttester,
3893  jvb,
3894  mcAlice,
3895  amt,
3896  payees,
3897  true,
3898  claimID,
3899  dst,
3900  signers));
3901 
3902  if (withClaim)
3903  {
3904  BEAST_EXPECT(transfer.has_not_happened());
3905 
3906  // need to submit a claim transactions
3907  scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3908  .close();
3909  }
3910 
3911  // this also checks that only 3 * split_reward was deducted from
3912  // scAlice (the payor account), since we passed payees.size() -
3913  // 1 to BalanceTransfer
3914  BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3915 
3916  // and make sure the account with deposit auth received nothing
3917  BEAST_EXPECT(last_signer.diff() == STAmount(0));
3918  }
3919 
3920  // coverage test: xchain_claim transaction with incorrect flag
3921  XEnv(*this, true)
3923  .close()
3924  .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3927  .close();
3928 
3929  // coverage test: xchain_claim transaction with xchain feature
3930  // disabled
3931  XEnv(*this, true)
3933  .disableFeature(featureXChainBridge)
3934  .close()
3935  .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3936  ter(temDISABLED))
3937  .close();
3938 
3939  // coverage test: XChainClaim::preclaim - isLockingChain = true;
3940  XEnv(*this)
3941  .tx(create_bridge(mcDoor, jvb))
3942  .close()
3943  .tx(xchain_claim(mcAlice, jvb, 1, XRP(1000), mcBob),
3945  }
3946 
3947  void
3949  {
3950  using namespace jtx;
3951 
3952  testcase("Bridge Create Account");
3953  XRPAmount tx_fee = txFee();
3954 
3955  // coverage test: transferHelper() - dst == src
3956  {
3957  XEnv scEnv(*this, true);
3958 
3959  auto const amt = XRP(111);
3960  auto const amt_plus_reward = amt + reward;
3961 
3964  .close();
3965 
3966  Balance door(scEnv, Account::master);
3967 
3968  // scEnv.tx(att_create_acct_batch1(1, amt,
3969  // Account::master)).close();
3970  scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2))
3971  .close();
3972  BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
3973  BEAST_EXPECT(
3974  scEnv.claimCount(jvb) == 0); // claim count is one less
3975 
3976  // scEnv.tx(att_create_acct_batch2(1, amt,
3977  // Account::master)).close();
3978  scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2, 2))
3979  .close();
3980  BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
3981  BEAST_EXPECT(
3982  scEnv.claimCount(jvb) == 1); // claim count was incremented
3983 
3984  BEAST_EXPECT(door.diff() == -reward);
3985  }
3986 
3987  // Check that creating an account with less than the minimum create
3988  // amount fails.
3989  {
3990  XEnv mcEnv(*this);
3991 
3992  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3993 
3994  Balance door(mcEnv, mcDoor);
3995  Balance carol(mcEnv, mcCarol);
3996 
3997  mcEnv
3999  mcCarol, jvb, scuAlice, XRP(19), reward),
4001  .close();
4002 
4003  BEAST_EXPECT(door.diff() == STAmount(0));
4004  BEAST_EXPECT(carol.diff() == -tx_fee);
4005  }
4006 
4007  // Check that creating an account with invalid flags fails.
4008  {
4009  XEnv mcEnv(*this);
4010 
4011  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4012 
4013  Balance door(mcEnv, mcDoor);
4014 
4015  mcEnv
4017  mcCarol, jvb, scuAlice, XRP(20), reward),
4020  .close();
4021 
4022  BEAST_EXPECT(door.diff() == STAmount(0));
4023  }
4024 
4025  // Check that creating an account with the XChainBridge feature
4026  // disabled fails.
4027  {
4028  XEnv mcEnv(*this);
4029 
4030  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4031 
4032  Balance door(mcEnv, mcDoor);
4033 
4036  mcCarol, jvb, scuAlice, XRP(20), reward),
4037  ter(temDISABLED))
4038  .close();
4039 
4040  BEAST_EXPECT(door.diff() == STAmount(0));
4041  }
4042 
4043  // Check that creating an account with a negative amount fails
4044  {
4045  XEnv mcEnv(*this);
4046 
4047  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4048 
4049  Balance door(mcEnv, mcDoor);
4050 
4051  mcEnv
4053  mcCarol, jvb, scuAlice, XRP(-20), reward),
4054  ter(temBAD_AMOUNT))
4055  .close();
4056 
4057  BEAST_EXPECT(door.diff() == STAmount(0));
4058  }
4059 
4060  // Check that creating an account with a negative reward fails
4061  {
4062  XEnv mcEnv(*this);
4063 
4064  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4065 
4066  Balance door(mcEnv, mcDoor);
4067 
4068  mcEnv
4070  mcCarol, jvb, scuAlice, XRP(20), XRP(-1)),
4071  ter(temBAD_AMOUNT))
4072  .close();
4073 
4074  BEAST_EXPECT(door.diff() == STAmount(0));
4075  }
4076 
4077  // Check that door account can't lock funds onto itself
4078  {
4079  XEnv mcEnv(*this);
4080 
4081  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4082 
4083  Balance door(mcEnv, mcDoor);
4084 
4085  mcEnv
4087  mcDoor, jvb, scuAlice, XRP(20), XRP(1)),
4089  .close();
4090 
4091  BEAST_EXPECT(door.diff() == -tx_fee);
4092  }
4093 
4094  // Check that reward matches the amount specified in bridge
4095  {
4096  XEnv mcEnv(*this);
4097 
4098  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4099 
4100  Balance door(mcEnv, mcDoor);
4101 
4102  mcEnv
4104  mcCarol, jvb, scuAlice, XRP(20), XRP(2)),
4106  .close();
4107 
4108  BEAST_EXPECT(door.diff() == STAmount(0));
4109  }
4110  }
4111 
4112  void
4114  {
4115  using namespace jtx;
4116  XRPAmount res0 = reserve(0);
4117  XRPAmount tx_fee = txFee();
4118 
4119  testcase("Fee dips into reserve");
4120 
4121  // commit where the fee dips into the reserve, this should succeed
4122  XEnv(*this)
4123  .tx(create_bridge(mcDoor, jvb))
4124  .fund(res0 + one_xrp + tx_fee - drops(1), mcuAlice)
4125  .close()
4126  .tx(xchain_commit(mcuAlice, jvb, 1, one_xrp, scBob),
4127  ter(tesSUCCESS));
4128 
4129  // commit where the commit amount drips into the reserve, this should
4130  // fail
4131  XEnv(*this)
4132  .tx(create_bridge(mcDoor, jvb))
4133  .fund(res0 + one_xrp - drops(1), mcuAlice)
4134  .close()
4135  .tx(xchain_commit(mcuAlice, jvb, 1, one_xrp, scBob),
4137 
4138  auto const minAccountCreate = XRP(20);
4139 
4140  // account create commit where the fee dips into the reserve,
4141  // this should succeed
4142  XEnv(*this)
4143  .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4144  .fund(
4145  res0 + tx_fee + minAccountCreate + reward - drops(1), mcuAlice)
4146  .close()
4148  mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4149  ter(tesSUCCESS));
4150 
4151  // account create commit where the commit dips into the reserve,
4152  // this should fail
4153  XEnv(*this)
4154  .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4155  .fund(res0 + minAccountCreate + reward - drops(1), mcuAlice)
4156  .close()
4158  mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4160  }
4161 
4162  void
4164  {
4165  using namespace jtx;
4166 
4167  testcase("Bridge Delete Door Account");
4168 
4169  auto const acctDelFee{
4170  drops(XEnv(*this).env_.current()->fees().increment)};
4171 
4172  // Deleting an account that owns bridge should fail
4173  {
4174  XEnv mcEnv(*this);
4175 
4176  mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1))).close();
4177 
4178  // We don't allow an account to be deleted if its sequence
4179  // number is within 256 of the current ledger.
4180  for (size_t i = 0; i < 256; ++i)
4181  mcEnv.close();
4182 
4183  // try to delete mcDoor, send funds to mcAlice
4184  mcEnv.tx(
4186  fee(acctDelFee),
4188  }
4189 
4190  // Deleting an account that owns a claim id should fail
4191  {
4192  XEnv scEnv(*this, true);
4193 
4195  .close()
4197  .close();
4198 
4199  // We don't allow an account to be deleted if its sequence
4200  // number is within 256 of the current ledger.
4201  for (size_t i = 0; i < 256; ++i)
4202  scEnv.close();
4203 
4204  // try to delete scAlice, send funds to scBob
4205  scEnv.tx(
4207  fee(acctDelFee),
4209  }
4210  }
4211 
4212  void
4213  run() override
4214  {
4215  testXChainBridgeExtraFields();
4216  testXChainCreateBridge();
4217  testXChainBridgeCreateConstraints();
4218  testXChainCreateBridgeMatrix();
4219  testXChainModifyBridge();
4220  testXChainCreateClaimID();
4221  testXChainCommit();
4222  testXChainAddAttestation();
4223  testXChainAddClaimNonBatchAttestation();
4224  testXChainAddAccountCreateNonBatchAttestation();
4225  testXChainClaim();
4226  testXChainCreateAccount();
4227  testFeeDipsIntoReserve();
4228  testXChainDeleteDoor();
4229  }
4230 };
4231 
4232 // -----------------------------------------------------------
4233 // -----------------------------------------------------------
4234 struct XChainSim_test : public beast::unit_test::suite,
4236 {
4237 private:
4238  static constexpr size_t num_signers = 5;
4239 
4240  // --------------------------------------------------
4241  enum class WithClaim { no, yes };
4242  struct Transfer
4243  {
4248  bool a2b; // direction of transfer
4249  WithClaim with_claim{WithClaim::no};
4250  uint32_t claim_id{0};
4252  };
4253 
4255  {
4260  bool a2b;
4261  uint32_t claim_id{0};
4263  };
4264 
4266  using BridgeID = BridgeDef const*;
4267 
4268  // tracking chain state
4269  // --------------------
4271  {
4272  STAmount startAmount{0};
4273  STAmount expectedDiff{0};
4274 
4275  void
4276  init(ENV& env, jtx::Account const& acct)
4277  {
4278  startAmount = env.balance(acct);
4279  expectedDiff = STAmount(0);
4280  }
4281 
4282  bool
4283  verify(ENV& env, jtx::Account const& acct) const
4284  {
4285  STAmount diff{env.balance(acct) - startAmount};
4286  bool check = diff == expectedDiff;
4287  return check;
4288  }
4289  };
4290 
4291  // --------------------------------------------------
4293  {
4297 
4299  : env(env), tx_fee(env.env_.current()->fees().base)
4300  {
4301  }
4302 
4303  void
4304  sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec& claims)
4305  {
4306  for (auto const& c : claims)
4307  {
4308  env.tx(c).close();
4309  spendFee(bridge->signers[signer_idx].account);
4310  }
4311  claims.clear();
4312  }
4313 
4314  uint32_t
4316  size_t signer_idx,
4317  BridgeID bridge,
4318  CreateClaimVec& claims)
4319  {
4320  size_t num_successful = 0;
4321  for (auto const& c : claims)
4322  {
4323  env.tx(c).close();
4324  if (env.ter() == tesSUCCESS)
4325  {
4326  counters[bridge].signers.push_back(signer_idx);
4327  num_successful++;
4328  }
4329  spendFee(bridge->signers[signer_idx].account);
4330  }
4331  claims.clear();
4332  return num_successful;
4333  }
4334 
4335  void
4337  {
4338  bool callback_called;
4339 
4340  // we have this "do {} while" loop because we want to process
4341  // all the account create which can reach quorum at this time
4342  // stamp.
4343  do
4344  {
4345  callback_called = false;
4346  for (size_t i = 0; i < signers_attns.size(); ++i)
4347  {
4348  for (auto& [bridge, claims] : signers_attns[i])
4349  {
4350  sendAttestations(i, bridge, claims.xfer_claims);
4351 
4352  auto& c = counters[bridge];
4353  auto& create_claims =
4354  claims.create_claims[c.claim_count];
4355  auto num_attns = create_claims.size();
4356  if (num_attns)
4357  {
4358  c.num_create_attn_sent += sendCreateAttestations(
4359  i, bridge, create_claims);
4360  }
4361  assert(claims.create_claims[c.claim_count].empty());
4362  }
4363  }
4364  for (auto& [bridge, c] : counters)
4365  {
4366  if (c.num_create_attn_sent >= bridge->quorum)
4367  {
4368  callback_called = true;
4369  c.create_callbacks[c.claim_count](c.signers);
4370  ++c.claim_count;
4371  c.num_create_attn_sent = 0;
4372  c.signers.clear();
4373  }
4374  }
4375  } while (callback_called);
4376  }
4377 
4378  void
4379  init(jtx::Account const& acct)
4380  {
4381  accounts[acct].init(env, acct);
4382  }
4383 
4384  void
4386  jtx::Account const& acct,
4387  STAmount amt,
4388  std::uint64_t divisor = 1)
4389  {
4390  if (amt.issue() != xrpIssue())
4391  return;
4392  auto it = accounts.find(acct);
4393  if (it == accounts.end())
4394  {
4395  accounts[acct].init(env, acct);
4396  // we just looked up the account, so expectedDiff == 0
4397  }
4398  else
4399  {
4400  it->second.expectedDiff +=
4401  (divisor == 1 ? amt
4402  : divide(
4403  amt,
4404  STAmount(amt.issue(), divisor),
4405  amt.issue()));
4406  }
4407  }
4408 
4409  void
4410  spend(jtx::Account const& acct, STAmount amt, std::uint64_t times = 1)
4411  {
4412  if (amt.issue() != xrpIssue())
4413  return;
4414  receive(
4415  acct,
4416  times == 1
4417  ? -amt
4418  : -multiply(
4419  amt, STAmount(amt.issue(), times), amt.issue()));
4420  }
4421 
4422  void
4423  transfer(jtx::Account const& from, jtx::Account const& to, STAmount amt)
4424  {
4425  spend(from, amt);
4426  receive(to, amt);
4427  }
4428 
4429  void
4430  spendFee(jtx::Account const& acct, size_t times = 1)
4431  {
4432  spend(acct, tx_fee, times);
4433  }
4434 
4435  bool
4436  verify() const
4437  {
4438  for (auto const& [acct, state] : accounts)
4439  if (!state.verify(env, acct))
4440  return false;
4441  return true;
4442  }
4443 
4445  {
4446  using complete_cb =
4448 
4449  uint32_t claim_id{0};
4450  uint32_t create_count{0}; // for account create. First should be 1
4451  uint32_t claim_count{
4452  0}; // for account create. Increments after quorum for
4453  // current create_count (starts at 1) is reached.
4454 
4455  uint32_t num_create_attn_sent{0}; // for current claim_count
4458  };
4459 
4460  struct Claims
4461  {
4464  };
4465 
4468 
4474  };
4475 
4477  {
4478  ChainStateTracker(ENV& a_env, ENV& b_env) : a_(a_env), b_(b_env)
4479  {
4480  }
4481 
4482  bool
4483  verify() const
4484  {
4485  return a_.verify() && b_.verify();
4486  }
4487 
4488  void
4490  {
4491  a_.sendAttestations();
4492  b_.sendAttestations();
4493  }
4494 
4495  void
4496  init(jtx::Account const& acct)
4497  {
4498  a_.init(acct);
4499  b_.init(acct);
4500  }
4501 
4504  };
4505 
4506  enum SmState {
4513  };
4514 
4515  enum Act_Flags { af_a2b = 1 << 0 };
4516 
4517  // --------------------------------------------------
4518  template <class T>
4519  class SmBase
4520  {
4521  public:
4523  const std::shared_ptr<ChainStateTracker>& chainstate,
4524  const BridgeDef& bridge)
4525  : bridge_(bridge), st_(chainstate)
4526  {
4527  }
4528 
4531  {
4532  return static_cast<T&>(*this).a2b() ? st_->a_ : st_->b_;
4533  }
4534 
4537  {
4538  return static_cast<T&>(*this).a2b() ? st_->b_ : st_->a_;
4539  }
4540 
4541  jtx::Account const&
4543  {
4544  return static_cast<T&>(*this).a2b() ? bridge_.doorA : bridge_.doorB;
4545  }
4546 
4547  jtx::Account const&
4549  {
4550  return static_cast<T&>(*this).a2b() ? bridge_.doorB : bridge_.doorA;
4551  }
4552 
4553  protected:
4556  };
4557 
4558  // --------------------------------------------------
4559  class SmCreateAccount : public SmBase<SmCreateAccount>
4560  {
4561  public:
4563 
4565  const std::shared_ptr<ChainStateTracker>& chainstate,
4566  const BridgeDef& bridge,
4567  AccountCreate create)
4568  : Base(chainstate, bridge)
4569  , sm_state(st_initial)
4570  , cr(std::move(create))
4571  {
4572  }
4573 
4574  bool
4575  a2b() const
4576  {
4577  return cr.a2b;
4578  }
4579 
4580  uint32_t
4582  {
4583  ChainStateTrack& st = srcState();
4584  jtx::Account const& srcdoor = srcDoor();
4585 
4586  st.env
4588  cr.from, bridge_.jvb, cr.to, cr.amt, cr.reward))
4589  .close(); // needed for claim_id sequence to be correct'
4590  st.spendFee(cr.from);
4591  st.transfer(cr.from, srcdoor, cr.amt);
4592  st.transfer(cr.from, srcdoor, cr.reward);
4593 
4594  return ++st.counters[&bridge_].create_count;
4595  }
4596 
4597  void
4598  attest(uint64_t time, uint32_t rnd)
4599  {
4600  ChainStateTrack& st = destState();
4601 
4602  // check all signers, but start at a random one
4603  size_t i;
4604  for (i = 0; i < num_signers; ++i)
4605  {
4606  size_t signer_idx = (rnd + i) % num_signers;
4607 
4608  if (!(cr.attested[signer_idx]))
4609  {
4610  // enqueue one attestation for this signer
4611  cr.attested[signer_idx] = true;
4612 
4613  st.signers_attns[signer_idx][&bridge_]
4614  .create_claims[cr.claim_id - 1]
4615  .emplace_back(create_account_attestation(
4616  bridge_.signers[signer_idx].account,
4617  bridge_.jvb,
4618  cr.from,
4619  cr.amt,
4620  cr.reward,
4621  bridge_.signers[signer_idx].account,
4622  cr.a2b,
4623  cr.claim_id,
4624  cr.to,
4625  bridge_.signers[signer_idx]));
4626  break;
4627  }
4628  }
4629 
4630  if (i == num_signers)
4631  return; // did not attest
4632 
4633  auto& counters = st.counters[&bridge_];
4634  if (counters.create_callbacks.size() < cr.claim_id)
4635  counters.create_callbacks.resize(cr.claim_id);
4636 
4637  auto complete_cb = [&](std::vector<size_t> const& signers) {
4638  auto num_attestors = signers.size();
4639  st.env.close();
4640  assert(
4641  num_attestors <=
4642  std::count(cr.attested.begin(), cr.attested.end(), true));
4643  assert(num_attestors >= bridge_.quorum);
4644  assert(cr.claim_id - 1 == counters.claim_count);
4645 
4646  auto r = cr.reward;
4647  auto reward = divide(r, STAmount(num_attestors), r.issue());
4648 
4649  for (auto i : signers)
4650  st.receive(bridge_.signers[i].account, reward);
4651 
4652  st.spend(dstDoor(), reward, num_attestors);
4653  st.transfer(dstDoor(), cr.to, cr.amt);
4654  st.env.env_.memoize(cr.to);
4655  sm_state = st_completed;
4656  };
4657 
4658  counters.create_callbacks[cr.claim_id - 1] = std::move(complete_cb);
4659  }
4660 
4661  SmState
4662  advance(uint64_t time, uint32_t rnd)
4663  {
4664  switch (sm_state)
4665  {
4666  case st_initial:
4667  cr.claim_id = issue_account_create();
4668  sm_state = st_attesting;
4669  break;
4670 
4671  case st_attesting:
4672  attest(time, rnd);
4673  break;
4674 
4675  default:
4676  assert(0);
4677  break;
4678 
4679  case st_completed:
4680  break; // will get this once
4681  }
4682  return sm_state;
4683  }
4684 
4685  private:
4688  };
4689 
4690  // --------------------------------------------------
4691  class SmTransfer : public SmBase<SmTransfer>
4692  {
4693  public:
4695 
4697  const std::shared_ptr<ChainStateTracker>& chainstate,
4698  const BridgeDef& bridge,
4699  Transfer xfer)
4700  : Base(chainstate, bridge)
4701  , xfer(std::move(xfer))
4702  , sm_state(st_initial)
4703  {
4704  }
4705 
4706  bool
4707  a2b() const
4708  {
4709  return xfer.a2b;
4710  }
4711 
4712  uint32_t
4714  {
4715  ChainStateTrack& st = destState();
4716 
4717  st.env
4719  xfer.to, bridge_.jvb, bridge_.reward, xfer.from))
4720  .close(); // needed for claim_id sequence to be
4721  // correct'
4722  st.spendFee(xfer.to);
4723  return ++st.counters[&bridge_].claim_id;
4724  }
4725 
4726  void
4728  {
4729  ChainStateTrack& st = srcState();
4730  jtx::Account const& srcdoor = srcDoor();
4731 
4732  if (xfer.amt.issue() != xrpIssue())
4733  {
4734  st.env.tx(pay(srcdoor, xfer.from, xfer.amt));
4735  st.spendFee(srcdoor);
4736  }
4737  st.env.tx(xchain_commit(
4738  xfer.from,
4739  bridge_.jvb,
4740  xfer.claim_id,
4741  xfer.amt,
4742  xfer.with_claim == WithClaim::yes
4743  ? std::nullopt
4744  : std::optional<jtx::Account>(xfer.finaldest)));
4745  st.spendFee(xfer.from);
4746  st.transfer(xfer.from, srcdoor, xfer.amt);
4747  }
4748 
4749  void
4751  {
4752  auto r = bridge_.reward;
4753  auto reward = divide(r, STAmount(bridge_.quorum), r.issue());
4754 
4755  for (size_t i = 0; i < num_signers; ++i)
4756  {
4757  if (xfer.attested[i])
4758  st.receive(bridge_.signers[i].account, reward);
4759  }
4760  st.spend(xfer.to, reward, bridge_.quorum);
4761  }
4762 
4763  bool
4764  attest(uint64_t time, uint32_t rnd)
4765  {
4766  ChainStateTrack& st = destState();
4767 
4768  // check all signers, but start at a random one
4769  for (size_t i = 0; i < num_signers; ++i)
4770  {
4771  size_t signer_idx = (rnd + i) % num_signers;
4772  if (!(xfer.attested[signer_idx]))
4773  {
4774  // enqueue one attestation for this signer
4775  xfer.attested[signer_idx] = true;
4776 
4777  st.signers_attns[signer_idx][&bridge_]
4778  .xfer_claims.emplace_back(claim_attestation(
4779  bridge_.signers[signer_idx].account,
4780  bridge_.jvb,
4781  xfer.from,
4782  xfer.amt,
4783  bridge_.signers[signer_idx].account,
4784  xfer.a2b,
4785  xfer.claim_id,
4786  xfer.with_claim == WithClaim::yes
4787  ? std::nullopt
4788  : std::optional<jtx::Account>(xfer.finaldest),
4789  bridge_.signers[signer_idx]));
4790  break;
4791  }
4792  }
4793 
4794  // return true if quorum was reached, false otherwise
4795  bool quorum =
4796  std::count(xfer.attested.begin(), xfer.attested.end(), true) >=
4797  bridge_.quorum;
4798  if (quorum && xfer.with_claim == WithClaim::no)
4799  {
4800  distribute_reward(st);
4801  st.transfer(dstDoor(), xfer.finaldest, xfer.amt);
4802  }
4803  return quorum;
4804  }
4805 
4806  void
4808  {
4809  ChainStateTrack& st = destState();
4810  st.env.tx(xchain_claim(
4811  xfer.to, bridge_.jvb, xfer.claim_id, xfer.amt, xfer.finaldest));
4812  distribute_reward(st);
4813  st.transfer(dstDoor(), xfer.finaldest, xfer.amt);
4814  st.spendFee(xfer.to);
4815  }
4816 
4817  SmState
4818  advance(uint64_t time, uint32_t rnd)
4819  {
4820  switch (sm_state)
4821  {
4822  case st_initial:
4823  xfer.claim_id = create_claim_id();
4824  sm_state = st_claimid_created;
4825  break;
4826 
4827  case st_claimid_created:
4828  commit();
4829  sm_state = st_attesting;
4830  break;
4831 
4832  case st_attesting:
4833  sm_state = attest(time, rnd)
4834  ? (xfer.with_claim == WithClaim::yes ? st_attested
4835  : st_completed)
4836  : st_attesting;
4837  break;
4838 
4839  case st_attested:
4840  assert(xfer.with_claim == WithClaim::yes);
4841  claim();
4842  sm_state = st_completed;
4843  break;
4844 
4845  default:
4846  case st_completed:
4847  assert(0); // should have been removed
4848  break;
4849  }
4850  return sm_state;
4851  }
4852 
4853  private:
4856  };
4857 
4858  // --------------------------------------------------
4861 
4863 
4864  void
4866  uint64_t time,
4867  const std::shared_ptr<ChainStateTracker>& chainstate,
4868  BridgeDef const& bridge,
4869  Transfer transfer)
4870  {
4871  sm_.emplace_back(
4872  time, SmTransfer(chainstate, bridge, std::move(transfer)));
4873  }
4874 
4875  void
4876  ac(uint64_t time,
4877  const std::shared_ptr<ChainStateTracker>& chainstate,
4878  BridgeDef const& bridge,
4879  AccountCreate ac)
4880  {
4881  sm_.emplace_back(
4882  time, SmCreateAccount(chainstate, bridge, std::move(ac)));
4883  }
4884 
4885 public:
4886  void
4889  bool verify_balances = true)
4890  {
4891  using namespace jtx;
4892  uint64_t time = 0;
4893  std::mt19937 gen(27); // Standard mersenne_twister_engine
4895 
4896  while (!sm_.empty())
4897  {
4898  ++time;
4899  for (auto it = sm_.begin(); it != sm_.end();)
4900  {
4901  auto vis = [&](auto& sm) {
4902  uint32_t rnd = distrib(gen);
4903  return sm.advance(time, rnd);
4904  };
4905  auto& [t, sm] = *it;
4906  if (t <= time && std::visit(vis, sm) == st_completed)
4907  it = sm_.erase(it);
4908  else
4909  ++it;
4910  }
4911 
4912  // send attestations
4913  st->sendAttestations();
4914 
4915  // make sure all transactions have been applied
4916  st->a_.env.close();
4917  st->b_.env.close();
4918 
4919  if (verify_balances)
4920  {
4921  BEAST_EXPECT(st->verify());
4922  }
4923  }
4924  }
4925 
4926  void
4928  {
4929  using namespace jtx;
4930 
4931  testcase("Bridge usage simulation");
4932 
4933  XEnv mcEnv(*this);
4934  XEnv scEnv(*this, true);
4935 
4936  auto st = std::make_shared<ChainStateTracker>(mcEnv, scEnv);
4937 
4938  // create 10 accounts + door funded on both chains, and store
4939  // in ChainStateTracker the initial amount of these accounts
4940  Account doorXRPLocking, doorUSDLocking, doorUSDIssuing;
4941 
4942  constexpr size_t num_acct = 10;
4943  auto a = [&doorXRPLocking, &doorUSDLocking, &doorUSDIssuing]() {
4944  using namespace std::literals;
4945  std::vector<Account> result;
4946  result.reserve(num_acct);
4947  for (int i = 0; i < num_acct; ++i)
4948  result.emplace_back(
4949  "a"s + std::to_string(i),
4950  (i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
4951  result.emplace_back("doorXRPLocking");
4952  doorXRPLocking = result.back();
4953  result.emplace_back("doorUSDLocking");
4954  doorUSDLocking = result.back();
4955  result.emplace_back("doorUSDIssuing");
4956  doorUSDIssuing = result.back();
4957  return result;
4958  }();
4959 
4960  for (auto& acct : a)
4961  {
4962  STAmount amt{XRP(100000)};
4963 
4964  mcEnv.fund(amt, acct);
4965  scEnv.fund(amt, acct);
4966  }
4967  Account USDLocking{"USDLocking"};
4968  IOU usdLocking{USDLocking["USD"]};
4969  IOU usdIssuing{doorUSDIssuing["USD"]};
4970 
4971  mcEnv.fund(XRP(100000), USDLocking);
4972  mcEnv.close();
4973  mcEnv.tx(trust(doorUSDLocking, usdLocking(100000)));
4974  mcEnv.close();
4975  mcEnv.tx(pay(USDLocking, doorUSDLocking, usdLocking(50000)));
4976 
4977  for (int i = 0; i < a.size(); ++i)
4978  {
4979  auto& acct{a[i]};
4980  if (i < num_acct)
4981  {
4982  mcEnv.tx(trust(acct, usdLocking(100000)));
4983  scEnv.tx(trust(acct, usdIssuing(100000)));
4984  }
4985  st->init(acct);
4986  }
4987  for (auto& s : signers)
4988  st->init(s.account);
4989 
4990  st->b_.init(Account::master);
4991 
4992  // also create some unfunded accounts
4993  constexpr size_t num_ua = 20;
4994  auto ua = []() {
4995  using namespace std::literals;
4996  std::vector<Account> result;
4997  result.reserve(num_ua);
4998  for (int i = 0; i < num_ua; ++i)
4999  result.emplace_back(
5000  "ua"s + std::to_string(i),
5001  (i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
5002  return result;
5003  }();
5004 
5005  // initialize a bridge from a BridgeDef
5006  auto initBridge = [&mcEnv, &scEnv, &st](BridgeDef& bd) {
5007  bd.initBridge(mcEnv, scEnv);
5008  st->a_.spendFee(bd.doorA, 2);
5009  st->b_.spendFee(bd.doorB, 2);
5010  };
5011 
5012  // create XRP -> XRP bridge
5013  // ------------------------
5014  BridgeDef xrp_b{
5015  doorXRPLocking,
5016  xrpIssue(),
5018  xrpIssue(),
5019  XRP(1),
5020  XRP(20),
5021  quorum,
5022  signers,
5023  Json::nullValue};
5024 
5025  initBridge(xrp_b);
5026 
5027  // create USD -> USD bridge
5028  // ------------------------
5029  BridgeDef usd_b{
5030  doorUSDLocking,
5031  usdLocking,
5032  doorUSDIssuing,
5033  usdIssuing,
5034  XRP(1),
5035  XRP(20),
5036  quorum,
5037  signers,
5038  Json::nullValue};
5039 
5040  initBridge(usd_b);
5041 
5042  // try a single account create + transfer to validate the simulation
5043  // engine. Do the transfer 8 time steps after the account create, to
5044  // give time enough for ua[0] to be funded now so it can reserve
5045  // the claimID
5046  // -----------------------------------------------------------------
5047  ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, true});
5048  xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), true});
5049  runSimulation(st);
5050 
5051  // try the same thing in the other direction
5052  // -----------------------------------------
5053  ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, false});
5054  xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), false});
5055  runSimulation(st);
5056 
5057  // run multiple XRP transfers
5058  // --------------------------
5059  xfer(0, st, xrp_b, {a[0], a[0], a[1], XRP(6), true, WithClaim::no});
5060  xfer(1, st, xrp_b, {a[0], a[0], a[1], XRP(8), false, WithClaim::no});
5061  xfer(1, st, xrp_b, {a[1], a[1], a[1], XRP(1), true});
5062  xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(3), false});
5063  xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(5), false});
5064  xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(7), false, WithClaim::no});
5065  xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(9), true});
5066  runSimulation(st);
5067 
5068  // run one USD transfer
5069  // --------------------
5070  xfer(0, st, usd_b, {a[0], a[1], a[2], usdLocking(3), true});
5071  runSimulation(st);
5072 
5073  // run multiple USD transfers
5074  // --------------------------
5075  xfer(0, st, usd_b, {a[0], a[0], a[1], usdLocking(6), true});
5076  xfer(1, st, usd_b, {a[0], a[0], a[1], usdIssuing(8), false});
5077  xfer(1, st, usd_b, {a[1], a[1], a[1], usdLocking(1), true});
5078  xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(3), false});
5079  xfer(2, st, usd_b, {a[1], a[1], a[1], usdIssuing(5), false});
5080  xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(7), false});
5081  xfer(2, st, usd_b, {a[1], a[1], a[1], usdLocking(9), true});
5082  runSimulation(st);
5083 
5084  // run mixed transfers
5085  // -------------------
5086  xfer(0, st, xrp_b, {a[0], a[0], a[0], XRP(1), true});
5087  xfer(0, st, usd_b, {a[1], a[3], a[3], usdIssuing(3), false});
5088  xfer(0, st, usd_b, {a[3], a[2], a[1], usdIssuing(5), false});
5089 
5090  xfer(1, st, xrp_b, {a[0], a[0], a[0], XRP(4), false});
5091  xfer(1, st, xrp_b, {a[1], a[1], a[0], XRP(8), true});
5092  xfer(1, st, usd_b, {a[4], a[1], a[1], usdLocking(7), true});
5093 
5094  xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(7), true});
5095  xfer(3, st, xrp_b, {a[0], a[4], a[3], XRP(2), false});
5096  xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(9), true});
5097  xfer(3, st, usd_b, {a[3], a[1], a[1], usdIssuing(11), false});
5098  runSimulation(st);
5099 
5100  // run multiple account create to stress attestation batching
5101  // ----------------------------------------------------------
5102  ac(0, st, xrp_b, {a[0], ua[1], XRP(301), xrp_b.reward, true});
5103  ac(0, st, xrp_b, {a[1], ua[2], XRP(302), xrp_b.reward, true});
5104  ac(1, st, xrp_b, {a[0], ua[3], XRP(303), xrp_b.reward, true});
5105  ac(2, st, xrp_b, {a[1], ua[4], XRP(304), xrp_b.reward, true});
5106  ac(3, st, xrp_b, {a[0], ua[5], XRP(305), xrp_b.reward, true});
5107  ac(4, st, xrp_b, {a[1], ua[6], XRP(306), xrp_b.reward, true});
5108  ac(6, st, xrp_b, {a[0], ua[7], XRP(307), xrp_b.reward, true});
5109  ac(7, st, xrp_b, {a[2], ua[8], XRP(308), xrp_b.reward, true});
5110  ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5111  ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5112  ac(10, st, xrp_b, {a[0], ua[10], XRP(310), xrp_b.reward, true});
5113  ac(12, st, xrp_b, {a[0], ua[11], XRP(311), xrp_b.reward, true});
5114  ac(12, st, xrp_b, {a[3], ua[12], XRP(312), xrp_b.reward, true});
5115  ac(12, st, xrp_b, {a[4], ua[13], XRP(313), xrp_b.reward, true});
5116  ac(12, st, xrp_b, {a[3], ua[14], XRP(314), xrp_b.reward, true});
5117  ac(12, st, xrp_b, {a[6], ua[15], XRP(315), xrp_b.reward, true});
5118  ac(13, st, xrp_b, {a[7], ua[16], XRP(316), xrp_b.reward, true});
5119  ac(15, st, xrp_b, {a[3], ua[17], XRP(317), xrp_b.reward, true});
5120  runSimulation(st, true); // balances verification working now.
5121  }
5122 
5123  void
5124  run() override
5125  {
5126  testXChainSimulation();
5127  }
5128 };
5129 
5130 BEAST_DEFINE_TESTSUITE(XChain, app, ripple);
5131 BEAST_DEFINE_TESTSUITE(XChainSim, app, ripple);
5132 
5133 } // namespace ripple::test
ripple::test::XChainSim_test::ChainStateTrack::BridgeCounters::signers
std::vector< size_t > signers
Definition: XChain_test.cpp:4456
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:4248
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:4473
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:319
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:690
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:4876
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:559
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:4470
std::uniform_int_distribution
ripple::test::XChain_test::testXChainCreateAccount
void testXChainCreateAccount()
Definition: XChain_test.cpp:3948
ripple::test::XChainSim_test::SmTransfer::attest
bool attest(uint64_t time, uint32_t rnd)
Definition: XChain_test.cpp:4764
ripple::test::XChainSim_test::sm_
SmCont sm_
Definition: XChain_test.cpp:4862
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:320
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:4469
ripple::test::XChainSim_test::SmTransfer::create_claim_id
uint32_t create_claim_id()
Definition: XChain_test.cpp:4713
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:4270
ripple::test::XChain_test::testXChainAddAccountCreateNonBatchAttestation
void testXChainAddAccountCreateNonBatchAttestation()
Definition: XChain_test.cpp:2617
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:350
ripple::terNO_LINE
@ terNO_LINE
Definition: TER.h:212
functional
ripple::test::XChainSim_test::SmCreateAccount::SmCreateAccount
SmCreateAccount(const std::shared_ptr< ChainStateTracker > &chainstate, const BridgeDef &bridge, AccountCreate create)
Definition: XChain_test.cpp:4564
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:1515
ripple::test::XChainSim_test::AccountStateTrack::verify
bool verify(ENV &env, jtx::Account const &acct) const
Definition: XChain_test.cpp:4283
std::vector::reserve
T reserve(T... args)
ripple::test::XChainSim_test::AccountCreate::a2b
bool a2b
Definition: XChain_test.cpp:4260
ripple::test::XChain_test::testXChainDeleteDoor
void testXChainDeleteDoor()
Definition: XChain_test.cpp:4163
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:4581
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:4522
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:4385
vector
ripple::test::jtx::Env::enableFeature
void enableFeature(uint256 const feature)
Definition: Env.cpp:476
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:4691
ripple::temXCHAIN_EQUAL_DOOR_ACCOUNTS
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition: TER.h:130
ripple::test::XChainSim_test::ChainStateTracker::b_
ChainStateTrack b_
Definition: XChain_test.cpp:4503
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:1379
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:290
ripple::test::XChainSim_test::SmTransfer::SmTransfer
SmTransfer(const std::shared_ptr< ChainStateTracker > &chainstate, const BridgeDef &bridge, Transfer xfer)
Definition: XChain_test.cpp:4696
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:4472
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:318
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:241
ripple::test::XChainSim_test::testXChainSimulation
void testXChainSimulation()
Definition: XChain_test.cpp:4927
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:982
ripple::keylet::xChainClaimID
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition: Indexes.cpp:414
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:4855
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:4460
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:4536
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:4530
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:4542
ripple::SField::getJsonName
Json::StaticString const & getJsonName() const
Definition: SField.h:214
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:4258
ripple::sfXChainAccountClaimCount
const SF_UINT64 sfXChainAccountClaimCount
ripple::test::XChainSim_test::Transfer::finaldest
jtx::Account finaldest
Definition: XChain_test.cpp:4246
ripple::temBAD_ISSUER
@ temBAD_ISSUER
Definition: TER.h:92
ripple::test::XChain_test::testXChainClaim
void testXChainClaim()
Definition: XChain_test.cpp:2692
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:4462
ripple::test::XChain_test::testXChainAddClaimNonBatchAttestation
void testXChainAddClaimNonBatchAttestation()
Definition: XChain_test.cpp:2388
ripple::SField::jsonName
const Json::StaticString jsonName
Definition: SField.h:163
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:4686
ripple::test::XChainSim_test::SmCreateAccount::attest
void attest(uint64_t time, uint32_t rnd)
Definition: XChain_test.cpp:4598
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:135
ripple::test::XChainSim_test::SmBase
Definition: XChain_test.cpp:4519
ripple::test::Balance::diff
STAmount diff() const
Definition: XChain_test.cpp:261
ripple::test::XChain_test::run
void run() override
Definition: XChain_test.cpp:4213
ripple::sfXChainClaimID
const SF_UINT64 sfXChainClaimID
ripple::temXCHAIN_BRIDGE_BAD_ISSUES
@ temXCHAIN_BRIDGE_BAD_ISSUES
Definition: TER.h:132
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:4865
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:4509
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:4254
ripple::tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
@ tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
Definition: TER.h:332
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:110
ripple::test::XChainSim_test::Transfer::from
jtx::Account from
Definition: XChain_test.cpp:4244
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:4256
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:4818
ripple::asfDisableMaster
constexpr std::uint32_t asfDisableMaster
Definition: TxFlags.h:77
ripple::test::XChainSim_test::AccountCreate::reward
STAmount reward
Definition: XChain_test.cpp:4259
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:4457
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:296
ripple::test::XChainSim_test::ChainStateTrack::ChainStateTrack
ChainStateTrack(ENV &env)
Definition: XChain_test.cpp:4298
ripple::keylet::bridge
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition: Indexes.cpp:401
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:4423
ripple::test::XChainSim_test::run
void run() override
Definition: XChain_test.cpp:5124
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:4315
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:4662
ripple::STAmount
Definition: STAmount.h:46
ripple::tecUNFUNDED_PAYMENT
@ tecUNFUNDED_PAYMENT
Definition: TER.h:266
ripple::test::XChainSim_test::ChainStateTracker::ChainStateTracker
ChainStateTracker(ENV &a_env, ENV &b_env)
Definition: XChain_test.cpp:4478
ripple::test::XChainSim_test::WithClaim
WithClaim
Definition: XChain_test.cpp:4241
ripple::tecXCHAIN_ACCOUNT_CREATE_PAST
@ tecXCHAIN_ACCOUNT_CREATE_PAST
Definition: TER.h:328
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:4483
ripple::ValStatus::current
@ current
This was a new validation and was added.
ripple::temBAD_AMOUNT
@ temBAD_AMOUNT
Definition: TER.h:88
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:4266
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:4379
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:4510
ripple::test::XChainSim_test::ChainStateTrack::BridgeCounters
Definition: XChain_test.cpp:4444
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:4304
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:4887
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:4854
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:4410
ripple::test::XChainSim_test::SmTransfer::claim
void claim()
Definition: XChain_test.cpp:4807
ripple::test::XChainSim_test::SmBase::dstDoor
jtx::Account const & dstDoor()
Definition: XChain_test.cpp:4548
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:4554
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:4247
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:333
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:4512
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:331
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:4463
ripple::test::XChainSim_test::SmCreateAccount
Definition: XChain_test.cpp:4559
ripple::test::XChainSim_test::ChainStateTracker
Definition: XChain_test.cpp:4476
ripple::test::XChain_test::reserve
XRPAmount reserve(std::uint32_t count)
Definition: XChain_test.cpp:393
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:113
limits
ripple::test::XChainSim_test::Act_Flags
Act_Flags
Definition: XChain_test.cpp:4515
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:324
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:4506
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:399
std
STL namespace.
ripple::test::XChainSim_test::ChainStateTrack
Definition: XChain_test.cpp:4292
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:4489
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:280
ripple::no
@ no
Definition: Steps.h:42
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:36
ripple::test::XChainSim_test::Transfer
Definition: XChain_test.cpp:4242
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:4496
ripple::test::jtx::XChainBridgeObjects::scBob
const Account scBob
Definition: xchain_bridge.h:162
ripple::tecHAS_OBLIGATIONS
@ tecHAS_OBLIGATIONS
Definition: TER.h:298
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:286
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:4336
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:428
std::visit
T visit(T... args)
ripple::tecINSUFFICIENT_RESERVE
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:288
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:4436
ripple::test::XEnv
Definition: XChain_test.cpp:202
ripple::test::XChainSim_test::SmCreateAccount::a2b
bool a2b() const
Definition: XChain_test.cpp:4575
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:134
ripple::test::XChainSim_test::st_claimid_created
@ st_claimid_created
Definition: XChain_test.cpp:4508
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:4257
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:4276
ripple::test::BridgeDef::quorum
uint32_t quorum
Definition: XChain_test.cpp:364
ripple::test::XChain_test::testXChainCreateClaimID
void testXChainCreateClaimID()
Definition: XChain_test.cpp:1289
ripple::test::XChain_test
Definition: XChain_test.cpp:389
ripple::tecNO_ENTRY
@ tecNO_ENTRY
Definition: TER.h:287
std::list::end
T end(T... args)
ripple::temMALFORMED
@ temMALFORMED
Definition: TER.h:86
ripple::test::XChainSim_test::st_initial
@ st_initial
Definition: XChain_test.cpp:4507
ripple::test::XChainSim_test::ChainStateTrack::spendFee
void spendFee(jtx::Account const &acct, size_t times=1)
Definition: XChain_test.cpp:4430
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:4750
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:4502
ripple::test::jtx::Env::memoize
void memoize(Account const &account)
Associate AccountID with account.
Definition: Env.cpp:156
ripple::yes
@ yes
Definition: Steps.h:42
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:4687
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:4245
ripple::sfXChainBridge
const SF_XCHAIN_BRIDGE sfXChainBridge
ripple::temXCHAIN_BRIDGE_NONDOOR_OWNER
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
Definition: TER.h:133
ripple::test::jtx::XChainBridgeObjects::mcuDoor
const Account mcuDoor
Definition: xchain_bridge.h:169
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
ripple::tecXCHAIN_NO_SIGNERS_LIST
@ tecXCHAIN_NO_SIGNERS_LIST
Definition: TER.h:325
ripple::test::XChain_test::testFeeDipsIntoReserve
void testFeeDipsIntoReserve()
Definition: XChain_test.cpp:4113
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:4707
ripple::test::XChainSim_test::SmTransfer::commit
void commit()
Definition: XChain_test.cpp:4727
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:271
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:4511
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:4471
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::test::XChainSim_test::SmBase::st_
std::shared_ptr< ChainStateTracker > st_
Definition: XChain_test.cpp:4555
variant
ripple::test::XChainSim_test
Definition: XChain_test.cpp:4234
ripple::tecXCHAIN_INSUFF_CREATE_AMOUNT
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition: TER.h:327
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)
string