rippled
XChainBridge.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/app/paths/Flow.h>
21 #include <ripple/app/tx/impl/SignerEntries.h>
22 #include <ripple/app/tx/impl/Transactor.h>
23 #include <ripple/app/tx/impl/XChainBridge.h>
24 #include <ripple/basics/Log.h>
25 #include <ripple/basics/XRPAmount.h>
26 #include <ripple/basics/chrono.h>
27 #include <ripple/beast/utility/Journal.h>
28 #include <ripple/ledger/ApplyView.h>
29 #include <ripple/ledger/PaymentSandbox.h>
30 #include <ripple/ledger/View.h>
31 #include <ripple/protocol/AccountID.h>
32 #include <ripple/protocol/Feature.h>
33 #include <ripple/protocol/Indexes.h>
34 #include <ripple/protocol/PublicKey.h>
35 #include <ripple/protocol/SField.h>
36 #include <ripple/protocol/STAmount.h>
37 #include <ripple/protocol/STObject.h>
38 #include <ripple/protocol/STXChainBridge.h>
39 #include <ripple/protocol/TER.h>
40 #include <ripple/protocol/TxFlags.h>
41 #include <ripple/protocol/XChainAttestations.h>
42 #include <ripple/protocol/digest.h>
43 #include <ripple/protocol/st.h>
44 
45 #include <unordered_map>
46 #include <unordered_set>
47 
48 namespace ripple {
49 
50 /*
51  Bridges connect two independent ledgers: a "locking chain" and an "issuing
52  chain". An asset can be moved from the locking chain to the issuing chain by
53  putting it into trust on the locking chain, and issuing a "wrapped asset"
54  that represents the locked asset on the issuing chain.
55 
56  Note that a bridge is not an exchange. There is no exchange rate: one wrapped
57  asset on the issuing chain always represents one asset in trust on the
58  locking chain. The bridge also does not exchange an asset on the locking
59  chain for an asset on the issuing chain.
60 
61  A good model for thinking about bridges is a box that contains an infinite
62  number of "wrapped tokens". When a token from the locking chain
63  (locking-chain-token) is put into the box, a wrapped token is taken out of
64  the box and put onto the issuing chain (issuing-chain-token). No one can use
65  the locking-chain-token while it remains in the box. When an
66  issuing-chain-token is returned to the box, one locking-chain-token is taken
67  out of the box and put back onto the locking chain.
68 
69  This requires a way to put assets into trust on one chain (put a
70  locking-chain-token into the box). A regular XRP account is used for this.
71  This account is called a "door account". Much in the same way that a door is
72  used to go from one room to another, a door account is used to move from one
73  chain to another. This account will be jointly controlled by a set of witness
74  servers by using the ledger's multi-signature support. The master key will be
75  disabled. These witness servers are trusted in the sense that if a quorum of
76  them collude, they can steal the funds put into trust.
77 
78  This also requires a way to prove that assets were put into the box - either
79  a locking-chain-token on the locking chain or returning an
80  issuing-chain-token on the issuing chain. A set of servers called "witness
81  servers" fill this role. These servers watch the ledger for these
82  transactions, and attest that the given events happened on the different
83  chains by signing messages with the event information.
84 
85  There needs to be a way to prevent the attestations from the witness
86  servers from being used more than once. "Claim ids" fill this role. A claim
87  id must be acquired on the destination chain before the asset is "put into
88  the box" on the source chain. This claim id has a unique id, and once it is
89  destroyed it can never exist again (it's a simple counter). The attestations
90  reference this claim id, and are accumulated on the claim id. Once a quorum
91  is reached, funds can move. Once the funds move, the claim id is destroyed.
92 
93  Finally, a claim id requires that the sender has an account on the
94  destination chain. For some chains, this can be a problem - especially if
95  the wrapped asset represents XRP, and XRP is needed to create an account.
96  There's a bootstrap problem. To address this, there is a special transaction
97  used to create accounts. This transaction does not require a claim id.
98 
99  See the document "docs/bridge/spec.md" for a full description of how
100  bridges and their transactions work.
101 */
102 
103 namespace {
104 
105 // Check that the public key is allowed to sign for the given account. If the
106 // account does not exist on the ledger, then the public key must be the master
107 // key for the given account if it existed. Otherwise the key must be an enabled
108 // master key or a regular key for the existing account.
109 TER
110 checkAttestationPublicKey(
111  ReadView const& view,
113  AccountID const& attestationSignerAccount,
114  PublicKey const& pk,
115  beast::Journal j)
116 {
117  if (!signersList.contains(attestationSignerAccount))
118  {
119  return tecNO_PERMISSION;
120  }
121 
122  AccountID const accountFromPK = calcAccountID(pk);
123 
124  if (auto const sleAttestationSigningAccount =
125  view.read(keylet::account(attestationSignerAccount)))
126  {
127  if (accountFromPK == attestationSignerAccount)
128  {
129  // master key
130  if (sleAttestationSigningAccount->getFieldU32(sfFlags) &
132  {
133  JLOG(j.trace()) << "Attempt to add an attestation with "
134  "disabled master key.";
136  }
137  }
138  else
139  {
140  // regular key
141  if (std::optional<AccountID> regularKey =
142  (*sleAttestationSigningAccount)[~sfRegularKey];
143  regularKey != accountFromPK)
144  {
145  if (!regularKey)
146  {
147  JLOG(j.trace())
148  << "Attempt to add an attestation with "
149  "account present and non-present regular key.";
150  }
151  else
152  {
153  JLOG(j.trace()) << "Attempt to add an attestation with "
154  "account present and mismatched "
155  "regular key/public key.";
156  }
158  }
159  }
160  }
161  else
162  {
163  // account does not exist.
164  if (calcAccountID(pk) != attestationSignerAccount)
165  {
166  JLOG(j.trace())
167  << "Attempt to add an attestation with non-existant account "
168  "and mismatched pk/account pair.";
170  }
171  }
172 
173  return tesSUCCESS;
174 }
175 
176 // If there is a quorum of attestations for the given parameters, then
177 // return the reward accounts, otherwise return TER for the error.
178 // Also removes attestations that are no longer part of the signers list.
179 //
180 // Note: the dst parameter is what the attestations are attesting to, which
181 // is not always used (it is used when automatically triggering a transfer
182 // from an `addAttestation` transaction, it is not used in a `claim`
183 // transaction). If the `checkDst` parameter is `check`, the attestations
184 // must attest to this destination, if it is `ignore` then the `dst` of the
185 // attestations are not checked (as for a `claim` transaction)
186 
187 enum class CheckDst { check, ignore };
188 template <class TAttestation>
189 Expected<std::vector<AccountID>, TER>
190 claimHelper(
191  XChainAttestationsBase<TAttestation>& attestations,
192  ReadView const& view,
193  typename TAttestation::MatchFields const& toMatch,
194  CheckDst checkDst,
195  std::uint32_t quorum,
197  beast::Journal j)
198 {
199  // Remove attestations that are not valid signers. They may be no longer
200  // part of the signers list, or their master key may have been disabled,
201  // or their regular key may have changed
202  attestations.erase_if([&](auto const& a) {
203  return checkAttestationPublicKey(
204  view, signersList, a.keyAccount, a.publicKey, j) !=
205  tesSUCCESS;
206  });
207 
208  // Check if we have quorum for the amount specified on the new claimAtt
209  std::vector<AccountID> rewardAccounts;
210  rewardAccounts.reserve(attestations.size());
211  std::uint32_t weight = 0;
212  for (auto const& a : attestations)
213  {
214  auto const matchR = a.match(toMatch);
215  // The dest must match if claimHelper is being run as a result of an add
216  // attestation transaction. The dst does not need to match if the
217  // claimHelper is being run using an explicit claim transaction.
218  using enum AttestationMatch;
219  if (matchR == nonDstMismatch ||
220  (checkDst == CheckDst::check && matchR != match))
221  continue;
222  auto i = signersList.find(a.keyAccount);
223  if (i == signersList.end())
224  {
225  assert(0); // should have already been checked
226  continue;
227  }
228  weight += i->second;
229  rewardAccounts.push_back(a.rewardAccount);
230  }
231 
232  if (weight >= quorum)
233  return rewardAccounts;
234 
236 }
237 
269 struct OnNewAttestationResult
270 {
271  std::optional<std::vector<AccountID>> rewardAccounts;
272  // `changed` is true if the attestation collection changed in any way
273  // (added/removed/changed)
274  bool changed{false};
275 };
276 
277 template <class TAttestation>
278 [[nodiscard]] OnNewAttestationResult
279 onNewAttestations(
280  XChainAttestationsBase<TAttestation>& attestations,
281  ReadView const& view,
282  typename TAttestation::TSignedAttestation const* attBegin,
283  typename TAttestation::TSignedAttestation const* attEnd,
284  std::uint32_t quorum,
286  beast::Journal j)
287 {
288  bool changed = false;
289  for (auto att = attBegin; att != attEnd; ++att)
290  {
291  if (checkAttestationPublicKey(
292  view,
293  signersList,
294  att->attestationSignerAccount,
295  att->publicKey,
296  j) != tesSUCCESS)
297  {
298  // The checkAttestationPublicKey is not strictly necessary here (it
299  // should be checked in a preclaim step), but it would be bad to let
300  // this slip through if that changes, and the check is relatively
301  // cheap, so we check again
302  continue;
303  }
304 
305  auto const& claimSigningAccount = att->attestationSignerAccount;
306  if (auto i = std::find_if(
307  attestations.begin(),
308  attestations.end(),
309  [&](auto const& a) {
310  return a.keyAccount == claimSigningAccount;
311  });
312  i != attestations.end())
313  {
314  // existing attestation
315  // replace old attestation with new attestation
316  *i = TAttestation{*att};
317  changed = true;
318  }
319  else
320  {
321  attestations.emplace_back(*att);
322  changed = true;
323  }
324  }
325 
326  auto r = claimHelper(
327  attestations,
328  view,
329  typename TAttestation::MatchFields{*attBegin},
330  CheckDst::check,
331  quorum,
332  signersList,
333  j);
334 
335  if (!r.has_value())
336  return {std::nullopt, changed};
337 
338  return {std::move(r.value()), changed};
339 };
340 
341 // Check if there is a quorurm of attestations for the given amount and
342 // chain. If so return the reward accounts, if not return the tec code (most
343 // likely tecXCHAIN_CLAIM_NO_QUORUM)
344 Expected<std::vector<AccountID>, TER>
345 onClaim(
346  XChainClaimAttestations& attestations,
347  ReadView const& view,
348  STAmount const& sendingAmount,
349  bool wasLockingChainSend,
350  std::uint32_t quorum,
352  beast::Journal j)
353 {
354  XChainClaimAttestation::MatchFields toMatch{
355  sendingAmount, wasLockingChainSend, std::nullopt};
356  return claimHelper(
357  attestations, view, toMatch, CheckDst::ignore, quorum, signersList, j);
358 }
359 
360 enum class CanCreateDstPolicy { no, yes };
361 
362 enum class DepositAuthPolicy { normal, dstCanBypass };
363 
364 // Allow the fee to dip into the reserve. To support this, information about the
365 // submitting account needs to be fed to the transfer helper.
366 struct TransferHelperSubmittingAccountInfo
367 {
368  AccountID account;
369  STAmount preFeeBalance;
370  STAmount postFeeBalance;
371 };
372 
395 TER
396 transferHelper(
397  PaymentSandbox& psb,
398  AccountID const& src,
399  AccountID const& dst,
400  std::optional<std::uint32_t> const& dstTag,
401  std::optional<AccountID> const& claimOwner,
402  STAmount const& amt,
403  CanCreateDstPolicy canCreate,
404  DepositAuthPolicy depositAuthPolicy,
406  submittingAccountInfo,
407  beast::Journal j)
408 {
409  if (dst == src)
410  return tesSUCCESS;
411 
412  auto const dstK = keylet::account(dst);
413  if (auto sleDst = psb.read(dstK))
414  {
415  // Check dst tag and deposit auth
416 
417  if ((sleDst->getFlags() & lsfRequireDestTag) && !dstTag)
418  return tecDST_TAG_NEEDED;
419 
420  // If the destination is the claim owner, and this is a claim
421  // transaction, that's the dst account sending funds to itself. It
422  // can bypass deposit auth.
423  bool const canBypassDepositAuth = dst == claimOwner &&
424  depositAuthPolicy == DepositAuthPolicy::dstCanBypass;
425 
426  if (!canBypassDepositAuth && (sleDst->getFlags() & lsfDepositAuth) &&
427  !psb.exists(keylet::depositPreauth(dst, src)))
428  {
429  return tecNO_PERMISSION;
430  }
431  }
432  else if (!amt.native() || canCreate == CanCreateDstPolicy::no)
433  {
434  return tecNO_DST;
435  }
436 
437  if (amt.native())
438  {
439  auto const sleSrc = psb.peek(keylet::account(src));
440  assert(sleSrc);
441  if (!sleSrc)
442  return tecINTERNAL;
443 
444  {
445  auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
446  auto const reserve = psb.fees().accountReserve(ownerCount);
447 
448  auto const availableBalance = [&]() -> STAmount {
449  STAmount const curBal = (*sleSrc)[sfBalance];
450  // Checking that account == src and postFeeBalance == curBal is
451  // not strictly nessisary, but helps protect against future
452  // changes
453  if (!submittingAccountInfo ||
454  submittingAccountInfo->account != src ||
455  submittingAccountInfo->postFeeBalance != curBal)
456  return curBal;
457  return submittingAccountInfo->preFeeBalance;
458  }();
459 
460  if (availableBalance < amt + reserve)
461  {
462  return tecUNFUNDED_PAYMENT;
463  }
464  }
465 
466  auto sleDst = psb.peek(dstK);
467  if (!sleDst)
468  {
469  if (canCreate == CanCreateDstPolicy::no)
470  {
471  // Already checked, but OK to check again
472  return tecNO_DST;
473  }
474  if (amt < psb.fees().accountReserve(0))
475  {
476  JLOG(j.trace()) << "Insufficient payment to create account.";
477  return tecNO_DST_INSUF_XRP;
478  }
479 
480  // Create the account.
481  std::uint32_t const seqno{
482  psb.rules().enabled(featureDeletableAccounts) ? psb.seq() : 1};
483 
484  sleDst = std::make_shared<SLE>(dstK);
485  sleDst->setAccountID(sfAccount, dst);
486  sleDst->setFieldU32(sfSequence, seqno);
487 
488  psb.insert(sleDst);
489  }
490 
491  (*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt;
492  (*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt;
493  psb.update(sleSrc);
494  psb.update(sleDst);
495 
496  return tesSUCCESS;
497  }
498 
499  auto const result = flow(
500  psb,
501  amt,
502  src,
503  dst,
504  STPathSet{},
505  /*default path*/ true,
506  /*partial payment*/ false,
507  /*owner pays transfer fee*/ true,
508  /*offer crossing*/ false,
509  /*limit quality*/ std::nullopt,
510  /*sendmax*/ std::nullopt,
511  j);
512 
513  if (auto const r = result.result();
514  isTesSuccess(r) || isTecClaim(r) || isTerRetry(r))
515  return r;
517 }
518 
524 enum class OnTransferFail {
526  removeClaim,
528  keepClaim
529 };
530 
531 struct FinalizeClaimHelperResult
532 {
534  std::optional<TER> mainFundsTer;
535  // TER for transfering the reward funds
536  std::optional<TER> rewardTer;
537  // TER for removing the sle (if is sle is to be removed)
538  std::optional<TER> rmSleTer;
539 
540  // Helper to check for overall success. If there wasn't overall success the
541  // individual ters can be used to decide what needs to be done.
542  bool
543  isTesSuccess() const
544  {
545  return mainFundsTer == tesSUCCESS && rewardTer == tesSUCCESS &&
546  (!rmSleTer || *rmSleTer == tesSUCCESS);
547  }
548 
549  TER
550  ter() const
551  {
552  if ((!mainFundsTer || *mainFundsTer == tesSUCCESS) &&
553  (!rewardTer || *rewardTer == tesSUCCESS) &&
554  (!rmSleTer || *rmSleTer == tesSUCCESS))
555  return tesSUCCESS;
556 
557  // if any phase return a tecINTERNAL or a tef, prefer returning those
558  // codes
559  if (mainFundsTer &&
560  (isTefFailure(*mainFundsTer) || *mainFundsTer == tecINTERNAL))
561  return *mainFundsTer;
562  if (rewardTer &&
563  (isTefFailure(*rewardTer) || *rewardTer == tecINTERNAL))
564  return *rewardTer;
565  if (rmSleTer && (isTefFailure(*rmSleTer) || *rmSleTer == tecINTERNAL))
566  return *rmSleTer;
567 
568  // Only after the tecINTERNAL and tef are checked, return the first
569  // non-success error code.
570  if (mainFundsTer && mainFundsTer != tesSUCCESS)
571  return *mainFundsTer;
572  if (rewardTer && rewardTer != tesSUCCESS)
573  return *rewardTer;
574  if (rmSleTer && rmSleTer != tesSUCCESS)
575  return *rmSleTer;
576  return tesSUCCESS;
577  }
578 };
579 
608 FinalizeClaimHelperResult
609 finalizeClaimHelper(
610  PaymentSandbox& outerSb,
611  STXChainBridge const& bridgeSpec,
612  AccountID const& dst,
613  std::optional<std::uint32_t> const& dstTag,
614  AccountID const& claimOwner,
615  STAmount const& sendingAmount,
616  AccountID const& rewardPoolSrc,
617  STAmount const& rewardPool,
618  std::vector<AccountID> const& rewardAccounts,
619  STXChainBridge::ChainType const srcChain,
620  Keylet const& claimIDKeylet,
621  OnTransferFail onTransferFail,
622  DepositAuthPolicy depositAuthPolicy,
623  beast::Journal j)
624 {
625  FinalizeClaimHelperResult result;
626 
627  STXChainBridge::ChainType const dstChain =
628  STXChainBridge::otherChain(srcChain);
629  STAmount const thisChainAmount = [&] {
630  STAmount r = sendingAmount;
631  r.setIssue(bridgeSpec.issue(dstChain));
632  return r;
633  }();
634  auto const& thisDoor = bridgeSpec.door(dstChain);
635 
636  {
637  PaymentSandbox innerSb{&outerSb};
638  // If distributing the reward pool fails, the mainFunds transfer should
639  // be rolled back
640  //
641  // If the claimid is removed, the rewards should be distributed
642  // even if the mainFunds fails.
643  //
644  // If OnTransferFail::removeClaim, the claim should be removed even if
645  // the rewards cannot be distributed.
646 
647  // transfer funds to the dst
648  result.mainFundsTer = transferHelper(
649  innerSb,
650  thisDoor,
651  dst,
652  dstTag,
653  claimOwner,
654  thisChainAmount,
655  CanCreateDstPolicy::yes,
656  depositAuthPolicy,
657  std::nullopt,
658  j);
659 
660  if (!isTesSuccess(*result.mainFundsTer) &&
661  onTransferFail == OnTransferFail::keepClaim)
662  {
663  return result;
664  }
665 
666  // handle the reward pool
667  result.rewardTer = [&]() -> TER {
668  if (rewardAccounts.empty())
669  return tesSUCCESS;
670 
671  // distribute the reward pool
672  // if the transfer failed, distribute the pool for "OnTransferFail"
673  // cases (the attesters did their job)
674  STAmount const share = [&] {
675  STAmount const den{rewardAccounts.size()};
676  return divide(rewardPool, den, rewardPool.issue());
677  }();
678  STAmount distributed = rewardPool.zeroed();
679  for (auto const& rewardAccount : rewardAccounts)
680  {
681  auto const thTer = transferHelper(
682  innerSb,
683  rewardPoolSrc,
684  rewardAccount,
685  /*dstTag*/ std::nullopt,
686  // claim owner is not relevant to distributing rewards
687  /*claimOwner*/ std::nullopt,
688  share,
689  CanCreateDstPolicy::no,
690  DepositAuthPolicy::normal,
691  std::nullopt,
692  j);
693 
694  if (thTer == tecUNFUNDED_PAYMENT || thTer == tecINTERNAL)
695  return thTer;
696 
697  if (isTesSuccess(thTer))
698  distributed += share;
699 
700  // let txn succeed if error distributing rewards (other than
701  // inability to pay)
702  }
703 
704  if (distributed > rewardPool)
705  return tecINTERNAL;
706 
707  return tesSUCCESS;
708  }();
709 
710  if (!isTesSuccess(*result.rewardTer) &&
711  (onTransferFail == OnTransferFail::keepClaim ||
712  *result.rewardTer == tecINTERNAL))
713  {
714  return result;
715  }
716 
717  if (!isTesSuccess(*result.mainFundsTer) ||
718  isTesSuccess(*result.rewardTer))
719  {
720  // Note: if the mainFunds transfer succeeds and the result transfer
721  // fails, we don't apply the inner sandbox (i.e. the mainTransfer is
722  // rolled back)
723  innerSb.apply(outerSb);
724  }
725  }
726 
727  if (auto const sleClaimID = outerSb.peek(claimIDKeylet))
728  {
729  auto const cidOwner = (*sleClaimID)[sfAccount];
730  {
731  // Remove the claim id
732  auto const sleOwner = outerSb.peek(keylet::account(cidOwner));
733  auto const page = (*sleClaimID)[sfOwnerNode];
734  if (!outerSb.dirRemove(
735  keylet::ownerDir(cidOwner), page, sleClaimID->key(), true))
736  {
737  JLOG(j.fatal())
738  << "Unable to delete xchain seq number from owner.";
739  result.rmSleTer = tefBAD_LEDGER;
740  return result;
741  }
742 
743  // Remove the claim id from the ledger
744  outerSb.erase(sleClaimID);
745 
746  adjustOwnerCount(outerSb, sleOwner, -1, j);
747  }
748  }
749 
750  return result;
751 }
752 
763 getSignersListAndQuorum(
764  ReadView const& view,
765  SLE const& sleBridge,
766  beast::Journal j)
767 {
770 
771  AccountID const thisDoor = sleBridge[sfAccount];
772  auto const sleDoor = [&] { return view.read(keylet::account(thisDoor)); }();
773 
774  if (!sleDoor)
775  {
776  return {r, q, tecINTERNAL};
777  }
778 
779  auto const sleS = view.read(keylet::signers(sleBridge[sfAccount]));
780  if (!sleS)
781  {
782  return {r, q, tecXCHAIN_NO_SIGNERS_LIST};
783  }
784  q = (*sleS)[sfSignerQuorum];
785 
786  auto const accountSigners = SignerEntries::deserialize(*sleS, j, "ledger");
787 
788  if (!accountSigners)
789  {
790  return {r, q, tecINTERNAL};
791  }
792 
793  for (auto const& as : *accountSigners)
794  {
795  r[as.account] = as.weight;
796  }
797 
798  return {std::move(r), q, tesSUCCESS};
799 };
800 
801 template <class R, class F>
803 readOrpeekBridge(F&& getter, STXChainBridge const& bridgeSpec)
804 {
805  auto tryGet = [&](STXChainBridge::ChainType ct) -> std::shared_ptr<R> {
806  if (auto r = getter(bridgeSpec, ct))
807  {
808  if ((*r)[sfXChainBridge] == bridgeSpec)
809  return r;
810  }
811  return nullptr;
812  };
813  if (auto r = tryGet(STXChainBridge::ChainType::locking))
814  return r;
815  return tryGet(STXChainBridge::ChainType::issuing);
816 }
817 
819 peekBridge(ApplyView& v, STXChainBridge const& bridgeSpec)
820 {
821  return readOrpeekBridge<SLE>(
822  [&v](STXChainBridge const& b, STXChainBridge::ChainType ct)
823  -> std::shared_ptr<SLE> { return v.peek(keylet::bridge(b, ct)); },
824  bridgeSpec);
825 }
826 
828 readBridge(ReadView const& v, STXChainBridge const& bridgeSpec)
829 {
830  return readOrpeekBridge<SLE const>(
831  [&v](STXChainBridge const& b, STXChainBridge::ChainType ct)
833  return v.read(keylet::bridge(b, ct));
834  },
835  bridgeSpec);
836 }
837 
838 // Precondition: all the claims in the range are consistent. They must sign for
839 // the same event (amount, sending account, claim id, etc).
840 template <class TIter>
841 TER
842 applyClaimAttestations(
843  ApplyView& view,
844  RawView& rawView,
845  TIter attBegin,
846  TIter attEnd,
847  STXChainBridge const& bridgeSpec,
848  STXChainBridge::ChainType const srcChain,
850  std::uint32_t quorum,
851  beast::Journal j)
852 {
853  if (attBegin == attEnd)
854  return tesSUCCESS;
855 
856  PaymentSandbox psb(&view);
857 
858  auto const claimIDKeylet =
859  keylet::xChainClaimID(bridgeSpec, attBegin->claimID);
860 
861  struct ScopeResult
862  {
863  OnNewAttestationResult newAttResult;
864  STAmount rewardAmount;
865  AccountID cidOwner;
866  };
867 
868  auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
869  // This lambda is ugly - admittedly. The purpose of this lambda is to
870  // limit the scope of sles so they don't overlap with
871  // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
872  // views, it's important that the sle's lifetime doesn't overlap.
873  auto const sleClaimID = psb.peek(claimIDKeylet);
874  if (!sleClaimID)
876 
877  // Add claims that are part of the signer's list to the "claims" vector
879  atts.reserve(std::distance(attBegin, attEnd));
880  for (auto att = attBegin; att != attEnd; ++att)
881  {
882  if (!signersList.contains(att->attestationSignerAccount))
883  continue;
884  atts.push_back(*att);
885  }
886 
887  if (atts.empty())
888  {
890  }
891 
892  AccountID const otherChainSource = (*sleClaimID)[sfOtherChainSource];
893  if (attBegin->sendingAccount != otherChainSource)
894  {
896  }
897 
898  {
899  STXChainBridge::ChainType const dstChain =
900  STXChainBridge::otherChain(srcChain);
901 
902  STXChainBridge::ChainType const attDstChain =
903  STXChainBridge::dstChain(attBegin->wasLockingChainSend);
904 
905  if (attDstChain != dstChain)
906  {
908  }
909  }
910 
911  XChainClaimAttestations curAtts{
912  sleClaimID->getFieldArray(sfXChainClaimAttestations)};
913 
914  auto const newAttResult = onNewAttestations(
915  curAtts,
916  view,
917  &atts[0],
918  &atts[0] + atts.size(),
919  quorum,
920  signersList,
921  j);
922 
923  // update the claim id
924  sleClaimID->setFieldArray(
925  sfXChainClaimAttestations, curAtts.toSTArray());
926  psb.update(sleClaimID);
927 
928  return ScopeResult{
929  newAttResult,
930  (*sleClaimID)[sfSignatureReward],
931  (*sleClaimID)[sfAccount]};
932  }();
933 
934  if (!scopeResult.has_value())
935  return scopeResult.error();
936 
937  auto const& [newAttResult, rewardAmount, cidOwner] = scopeResult.value();
938  auto const& [rewardAccounts, attListChanged] = newAttResult;
939  if (rewardAccounts && attBegin->dst)
940  {
941  auto const r = finalizeClaimHelper(
942  psb,
943  bridgeSpec,
944  *attBegin->dst,
945  /*dstTag*/ std::nullopt,
946  cidOwner,
947  attBegin->sendingAmount,
948  cidOwner,
949  rewardAmount,
950  *rewardAccounts,
951  srcChain,
952  claimIDKeylet,
953  OnTransferFail::keepClaim,
954  DepositAuthPolicy::normal,
955  j);
956 
957  auto const rTer = r.ter();
958 
959  if (!isTesSuccess(rTer) &&
960  (!attListChanged || rTer == tecINTERNAL || rTer == tefBAD_LEDGER))
961  return rTer;
962  }
963 
964  psb.apply(rawView);
965 
966  return tesSUCCESS;
967 }
968 
969 template <class TIter>
970 TER
971 applyCreateAccountAttestations(
972  ApplyView& view,
973  RawView& rawView,
974  TIter attBegin,
975  TIter attEnd,
976  AccountID const& doorAccount,
977  Keylet const& doorK,
978  STXChainBridge const& bridgeSpec,
979  Keylet const& bridgeK,
980  STXChainBridge::ChainType const srcChain,
982  std::uint32_t quorum,
983  beast::Journal j)
984 {
985  if (attBegin == attEnd)
986  return tesSUCCESS;
987 
988  PaymentSandbox psb(&view);
989 
990  auto const claimCountResult = [&]() -> Expected<std::uint64_t, TER> {
991  auto const sleBridge = psb.peek(bridgeK);
992  if (!sleBridge)
993  return Unexpected(tecINTERNAL);
994 
995  return (*sleBridge)[sfXChainAccountClaimCount];
996  }();
997 
998  if (!claimCountResult.has_value())
999  return claimCountResult.error();
1000 
1001  std::uint64_t const claimCount = claimCountResult.value();
1002 
1003  if (attBegin->createCount <= claimCount)
1004  {
1006  }
1007  if (attBegin->createCount >= claimCount + xbridgeMaxAccountCreateClaims)
1008  {
1009  // Limit the number of claims on the account
1011  }
1012 
1013  {
1014  STXChainBridge::ChainType const dstChain =
1015  STXChainBridge::otherChain(srcChain);
1016 
1017  STXChainBridge::ChainType const attDstChain =
1018  STXChainBridge::dstChain(attBegin->wasLockingChainSend);
1019 
1020  if (attDstChain != dstChain)
1021  {
1022  return tecXCHAIN_WRONG_CHAIN;
1023  }
1024  }
1025 
1026  auto const claimIDKeylet =
1027  keylet::xChainCreateAccountClaimID(bridgeSpec, attBegin->createCount);
1028 
1029  struct ScopeResult
1030  {
1031  OnNewAttestationResult newAttResult;
1032  bool createCID;
1033  XChainCreateAccountAttestations curAtts;
1034  };
1035 
1036  auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
1037  // This lambda is ugly - admittedly. The purpose of this lambda is to
1038  // limit the scope of sles so they don't overlap with
1039  // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
1040  // views, it's important that the sle's lifetime doesn't overlap.
1041 
1042  // sleClaimID may be null. If it's null it isn't created until the end
1043  // of this function (if needed)
1044  auto const sleClaimID = psb.peek(claimIDKeylet);
1045  bool createCID = false;
1046  if (!sleClaimID)
1047  {
1048  createCID = true;
1049 
1050  auto const sleDoor = psb.peek(doorK);
1051  if (!sleDoor)
1052  return Unexpected(tecINTERNAL);
1053 
1054  // Check reserve
1055  auto const balance = (*sleDoor)[sfBalance];
1056  auto const reserve =
1057  psb.fees().accountReserve((*sleDoor)[sfOwnerCount] + 1);
1058 
1059  if (balance < reserve)
1061  }
1062 
1064  atts.reserve(std::distance(attBegin, attEnd));
1065  for (auto att = attBegin; att != attEnd; ++att)
1066  {
1067  if (!signersList.contains(att->attestationSignerAccount))
1068  continue;
1069  atts.push_back(*att);
1070  }
1071  if (atts.empty())
1072  {
1074  }
1075 
1076  XChainCreateAccountAttestations curAtts = [&] {
1077  if (sleClaimID)
1078  return XChainCreateAccountAttestations{
1079  sleClaimID->getFieldArray(
1081  return XChainCreateAccountAttestations{};
1082  }();
1083 
1084  auto const newAttResult = onNewAttestations(
1085  curAtts,
1086  view,
1087  &atts[0],
1088  &atts[0] + atts.size(),
1089  quorum,
1090  signersList,
1091  j);
1092 
1093  if (!createCID)
1094  {
1095  // Modify the object before it's potentially deleted, so the meta
1096  // data will include the new attestations
1097  if (!sleClaimID)
1098  return Unexpected(tecINTERNAL);
1099  sleClaimID->setFieldArray(
1100  sfXChainCreateAccountAttestations, curAtts.toSTArray());
1101  psb.update(sleClaimID);
1102  }
1103  return ScopeResult{newAttResult, createCID, curAtts};
1104  }();
1105 
1106  if (!scopeResult.has_value())
1107  return scopeResult.error();
1108 
1109  auto const& [attResult, createCID, curAtts] = scopeResult.value();
1110  auto const& [rewardAccounts, attListChanged] = attResult;
1111 
1112  // Account create transactions must happen in order
1113  if (rewardAccounts && claimCount + 1 == attBegin->createCount)
1114  {
1115  auto const r = finalizeClaimHelper(
1116  psb,
1117  bridgeSpec,
1118  attBegin->toCreate,
1119  /*dstTag*/ std::nullopt,
1120  doorAccount,
1121  attBegin->sendingAmount,
1122  /*rewardPoolSrc*/ doorAccount,
1123  attBegin->rewardAmount,
1124  *rewardAccounts,
1125  srcChain,
1126  claimIDKeylet,
1127  OnTransferFail::removeClaim,
1128  DepositAuthPolicy::normal,
1129  j);
1130 
1131  auto const rTer = r.ter();
1132 
1133  if (!isTesSuccess(rTer))
1134  {
1135  if (rTer == tecINTERNAL || rTer == tecUNFUNDED_PAYMENT ||
1136  isTefFailure(rTer))
1137  return rTer;
1138  }
1139  // Move past this claim id even if it fails, so it doesn't block
1140  // subsequent claim ids
1141  auto const sleBridge = psb.peek(bridgeK);
1142  if (!sleBridge)
1143  return tecINTERNAL;
1144  (*sleBridge)[sfXChainAccountClaimCount] = attBegin->createCount;
1145  psb.update(sleBridge);
1146  }
1147  else if (createCID)
1148  {
1149  auto const createdSleClaimID = std::make_shared<SLE>(claimIDKeylet);
1150  (*createdSleClaimID)[sfAccount] = doorAccount;
1151  (*createdSleClaimID)[sfXChainBridge] = bridgeSpec;
1152  (*createdSleClaimID)[sfXChainAccountCreateCount] =
1153  attBegin->createCount;
1154  createdSleClaimID->setFieldArray(
1155  sfXChainCreateAccountAttestations, curAtts.toSTArray());
1156 
1157  // Add to owner directory of the door account
1158  auto const page = psb.dirInsert(
1159  keylet::ownerDir(doorAccount),
1160  claimIDKeylet,
1161  describeOwnerDir(doorAccount));
1162  if (!page)
1163  return tecDIR_FULL;
1164  (*createdSleClaimID)[sfOwnerNode] = *page;
1165 
1166  auto const sleDoor = psb.peek(doorK);
1167  if (!sleDoor)
1168  return tecINTERNAL;
1169 
1170  // Reserve was already checked
1171  adjustOwnerCount(psb, sleDoor, 1, j);
1172  psb.insert(createdSleClaimID);
1173  psb.update(sleDoor);
1174  }
1175 
1176  psb.apply(rawView);
1177 
1178  return tesSUCCESS;
1179 }
1180 
1181 template <class TAttestation>
1183 toClaim(STTx const& tx)
1184 {
1185  static_assert(
1186  std::is_same_v<TAttestation, Attestations::AttestationClaim> ||
1187  std::is_same_v<TAttestation, Attestations::AttestationCreateAccount>);
1188 
1189  try
1190  {
1191  STObject o{tx};
1192  o.setAccountID(sfAccount, o[sfOtherChainSource]);
1193  return TAttestation(o);
1194  }
1195  catch (...)
1196  {
1197  }
1198  return std::nullopt;
1199 }
1200 
1201 template <class TAttestation>
1202 NotTEC
1203 attestationPreflight(PreflightContext const& ctx)
1204 {
1205  if (!ctx.rules.enabled(featureXChainBridge))
1206  return temDISABLED;
1207 
1208  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
1209  return ret;
1210 
1211  if (ctx.tx.getFlags() & tfUniversalMask)
1212  return temINVALID_FLAG;
1213 
1214  auto const att = toClaim<TAttestation>(ctx.tx);
1215  if (!att)
1216  return temMALFORMED;
1217 
1218  STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge];
1219  if (!att->verify(bridgeSpec))
1220  return temXCHAIN_BAD_PROOF;
1221  if (!att->validAmounts())
1222  return temXCHAIN_BAD_PROOF;
1223 
1224  if (att->sendingAmount.signum() <= 0)
1225  return temXCHAIN_BAD_PROOF;
1226  auto const expectedIssue =
1227  bridgeSpec.issue(STXChainBridge::srcChain(att->wasLockingChainSend));
1228  if (att->sendingAmount.issue() != expectedIssue)
1229  return temXCHAIN_BAD_PROOF;
1230 
1231  return preflight2(ctx);
1232 }
1233 
1234 template <class TAttestation>
1235 TER
1236 attestationPreclaim(PreclaimContext const& ctx)
1237 {
1238  auto const att = toClaim<TAttestation>(ctx.tx);
1239  if (!att)
1240  return tecINTERNAL; // checked in preflight
1241 
1242  STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge];
1243  auto const sleBridge = readBridge(ctx.view, bridgeSpec);
1244  if (!sleBridge)
1245  {
1246  return tecNO_ENTRY;
1247  }
1248 
1249  AccountID const attestationSignerAccount{
1250  ctx.tx[sfAttestationSignerAccount]};
1251  PublicKey const pk{ctx.tx[sfPublicKey]};
1252 
1253  // signersList is a map from account id to weights
1254  auto const [signersList, quorum, slTer] =
1255  getSignersListAndQuorum(ctx.view, *sleBridge, ctx.j);
1256 
1257  if (!isTesSuccess(slTer))
1258  return slTer;
1259 
1260  return checkAttestationPublicKey(
1261  ctx.view, signersList, attestationSignerAccount, pk, ctx.j);
1262 }
1263 
1264 template <class TAttestation>
1265 TER
1266 attestationDoApply(ApplyContext& ctx)
1267 {
1268  auto const att = toClaim<TAttestation>(ctx.tx);
1269  if (!att)
1270  // Should already be checked in preflight
1271  return tecINTERNAL;
1272 
1273  STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge];
1274 
1275  struct ScopeResult
1276  {
1277  STXChainBridge::ChainType srcChain;
1279  std::uint32_t quorum;
1280  AccountID thisDoor;
1281  Keylet bridgeK;
1282  };
1283 
1284  auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
1285  // This lambda is ugly - admittedly. The purpose of this lambda is to
1286  // limit the scope of sles so they don't overlap with
1287  // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
1288  // views, it's important that the sle's lifetime doesn't overlap.
1289  auto sleBridge = readBridge(ctx.view(), bridgeSpec);
1290  if (!sleBridge)
1291  {
1292  return Unexpected(tecNO_ENTRY);
1293  }
1294  Keylet const bridgeK{ltBRIDGE, sleBridge->key()};
1295  AccountID const thisDoor = (*sleBridge)[sfAccount];
1296 
1298  {
1299  if (thisDoor == bridgeSpec.lockingChainDoor())
1301  else if (thisDoor == bridgeSpec.issuingChainDoor())
1303  else
1304  return Unexpected(tecINTERNAL);
1305  }
1306  STXChainBridge::ChainType const srcChain =
1307  STXChainBridge::otherChain(dstChain);
1308 
1309  // signersList is a map from account id to weights
1310  auto [signersList, quorum, slTer] =
1311  getSignersListAndQuorum(ctx.view(), *sleBridge, ctx.journal);
1312 
1313  if (!isTesSuccess(slTer))
1314  return Unexpected(slTer);
1315 
1316  return ScopeResult{
1317  srcChain, std::move(signersList), quorum, thisDoor, bridgeK};
1318  }();
1319 
1320  if (!scopeResult.has_value())
1321  return scopeResult.error();
1322 
1323  auto const& [srcChain, signersList, quorum, thisDoor, bridgeK] =
1324  scopeResult.value();
1325 
1326  static_assert(
1327  std::is_same_v<TAttestation, Attestations::AttestationClaim> ||
1328  std::is_same_v<TAttestation, Attestations::AttestationCreateAccount>);
1329 
1330  if constexpr (std::is_same_v<TAttestation, Attestations::AttestationClaim>)
1331  {
1332  return applyClaimAttestations(
1333  ctx.view(),
1334  ctx.rawView(),
1335  &*att,
1336  &*att + 1,
1337  bridgeSpec,
1338  srcChain,
1339  signersList,
1340  quorum,
1341  ctx.journal);
1342  }
1343  else if constexpr (std::is_same_v<
1344  TAttestation,
1345  Attestations::AttestationCreateAccount>)
1346  {
1347  return applyCreateAccountAttestations(
1348  ctx.view(),
1349  ctx.rawView(),
1350  &*att,
1351  &*att + 1,
1352  thisDoor,
1353  keylet::account(thisDoor),
1354  bridgeSpec,
1355  bridgeK,
1356  srcChain,
1357  signersList,
1358  quorum,
1359  ctx.journal);
1360  }
1361 }
1362 
1363 } // namespace
1364 //------------------------------------------------------------------------------
1365 
1366 NotTEC
1368 {
1369  if (!ctx.rules.enabled(featureXChainBridge))
1370  return temDISABLED;
1371 
1372  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
1373  return ret;
1374 
1375  if (ctx.tx.getFlags() & tfUniversalMask)
1376  return temINVALID_FLAG;
1377 
1378  auto const account = ctx.tx[sfAccount];
1379  auto const reward = ctx.tx[sfSignatureReward];
1380  auto const minAccountCreate = ctx.tx[~sfMinAccountCreateAmount];
1381  auto const bridgeSpec = ctx.tx[sfXChainBridge];
1382  // Doors must be distinct to help prevent transaction replay attacks
1383  if (bridgeSpec.lockingChainDoor() == bridgeSpec.issuingChainDoor())
1384  {
1386  }
1387 
1388  if (bridgeSpec.lockingChainDoor() != account &&
1389  bridgeSpec.issuingChainDoor() != account)
1390  {
1392  }
1393 
1394  if (isXRP(bridgeSpec.lockingChainIssue()) !=
1395  isXRP(bridgeSpec.issuingChainIssue()))
1396  {
1397  // Because ious and xrp have different numeric ranges, both the src and
1398  // dst issues must be both XRP or both IOU.
1400  }
1401 
1402  if (!isXRP(reward) || reward.signum() < 0)
1403  {
1405  }
1406 
1407  if (minAccountCreate &&
1408  ((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1409  !isXRP(bridgeSpec.lockingChainIssue()) ||
1410  !isXRP(bridgeSpec.issuingChainIssue())))
1411  {
1413  }
1414 
1415  if (isXRP(bridgeSpec.issuingChainIssue()))
1416  {
1417  // Issuing account must be the root account for XRP (which presumably
1418  // owns all the XRP). This is done so the issuing account can't "run
1419  // out" of wrapped tokens.
1420  static auto const rootAccount = calcAccountID(
1422  KeyType::secp256k1, generateSeed("masterpassphrase"))
1423  .first);
1424  if (bridgeSpec.issuingChainDoor() != rootAccount)
1425  {
1427  }
1428  }
1429  else
1430  {
1431  // Issuing account must be the issuer for non-XRP. This is done so the
1432  // issuing account can't "run out" of wrapped tokens.
1433  if (bridgeSpec.issuingChainDoor() !=
1434  bridgeSpec.issuingChainIssue().account)
1435  {
1437  }
1438  }
1439 
1440  if (bridgeSpec.lockingChainDoor() == bridgeSpec.lockingChainIssue().account)
1441  {
1442  // If the locking chain door is locking their own asset, in some sense
1443  // nothing is being locked. Disallow this.
1445  }
1446 
1447  return preflight2(ctx);
1448 }
1449 
1450 TER
1452 {
1453  auto const account = ctx.tx[sfAccount];
1454  auto const bridgeSpec = ctx.tx[sfXChainBridge];
1455  STXChainBridge::ChainType const chainType =
1456  STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor());
1457 
1458  {
1459  auto hasBridge = [&](STXChainBridge::ChainType ct) -> bool {
1460  return ctx.view.exists(keylet::bridge(bridgeSpec, ct));
1461  };
1462 
1463  if (hasBridge(STXChainBridge::ChainType::issuing) ||
1465  {
1466  return tecDUPLICATE;
1467  }
1468  }
1469 
1470  if (!isXRP(bridgeSpec.issue(chainType)))
1471  {
1472  auto const sleIssuer =
1473  ctx.view.read(keylet::account(bridgeSpec.issue(chainType).account));
1474 
1475  if (!sleIssuer)
1476  return tecNO_ISSUER;
1477 
1478  // Allowing clawing back funds would break the bridge's invariant that
1479  // wrapped funds are always backed by locked funds
1480  if (sleIssuer->getFlags() & lsfAllowTrustLineClawback)
1481  return tecNO_PERMISSION;
1482  }
1483 
1484  {
1485  // Check reserve
1486  auto const sleAcc = ctx.view.read(keylet::account(account));
1487  if (!sleAcc)
1488  return terNO_ACCOUNT;
1489 
1490  auto const balance = (*sleAcc)[sfBalance];
1491  auto const reserve =
1492  ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1);
1493 
1494  if (balance < reserve)
1495  return tecINSUFFICIENT_RESERVE;
1496  }
1497 
1498  return tesSUCCESS;
1499 }
1500 
1501 TER
1503 {
1504  auto const account = ctx_.tx[sfAccount];
1505  auto const bridgeSpec = ctx_.tx[sfXChainBridge];
1506  auto const reward = ctx_.tx[sfSignatureReward];
1507  auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
1508 
1509  auto const sleAcct = ctx_.view().peek(keylet::account(account));
1510  if (!sleAcct)
1511  return tecINTERNAL;
1512 
1513  STXChainBridge::ChainType const chainType =
1514  STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor());
1515 
1516  Keylet const bridgeKeylet = keylet::bridge(bridgeSpec, chainType);
1517  auto const sleBridge = std::make_shared<SLE>(bridgeKeylet);
1518 
1519  (*sleBridge)[sfAccount] = account;
1520  (*sleBridge)[sfSignatureReward] = reward;
1521  if (minAccountCreate)
1522  (*sleBridge)[sfMinAccountCreateAmount] = *minAccountCreate;
1523  (*sleBridge)[sfXChainBridge] = bridgeSpec;
1524  (*sleBridge)[sfXChainClaimID] = 0;
1525  (*sleBridge)[sfXChainAccountCreateCount] = 0;
1526  (*sleBridge)[sfXChainAccountClaimCount] = 0;
1527 
1528  // Add to owner directory
1529  {
1530  auto const page = ctx_.view().dirInsert(
1531  keylet::ownerDir(account), bridgeKeylet, describeOwnerDir(account));
1532  if (!page)
1533  return tecDIR_FULL;
1534  (*sleBridge)[sfOwnerNode] = *page;
1535  }
1536 
1537  adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal);
1538 
1539  ctx_.view().insert(sleBridge);
1540  ctx_.view().update(sleAcct);
1541 
1542  return tesSUCCESS;
1543 }
1544 
1545 //------------------------------------------------------------------------------
1546 
1547 NotTEC
1549 {
1550  if (!ctx.rules.enabled(featureXChainBridge))
1551  return temDISABLED;
1552 
1553  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
1554  return ret;
1555 
1556  if (ctx.tx.getFlags() & tfBridgeModifyMask)
1557  return temINVALID_FLAG;
1558 
1559  auto const account = ctx.tx[sfAccount];
1560  auto const reward = ctx.tx[~sfSignatureReward];
1561  auto const minAccountCreate = ctx.tx[~sfMinAccountCreateAmount];
1562  auto const bridgeSpec = ctx.tx[sfXChainBridge];
1563  bool const clearAccountCreate =
1565 
1566  if (!reward && !minAccountCreate && !clearAccountCreate)
1567  {
1568  // Must change something
1569  return temMALFORMED;
1570  }
1571 
1572  if (minAccountCreate && clearAccountCreate)
1573  {
1574  // Can't both clear and set account create in the same txn
1575  return temMALFORMED;
1576  }
1577 
1578  if (bridgeSpec.lockingChainDoor() != account &&
1579  bridgeSpec.issuingChainDoor() != account)
1580  {
1582  }
1583 
1584  if (reward && (!isXRP(*reward) || reward->signum() < 0))
1585  {
1587  }
1588 
1589  if (minAccountCreate &&
1590  ((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1591  !isXRP(bridgeSpec.lockingChainIssue()) ||
1592  !isXRP(bridgeSpec.issuingChainIssue())))
1593  {
1595  }
1596 
1597  return preflight2(ctx);
1598 }
1599 
1600 TER
1602 {
1603  auto const account = ctx.tx[sfAccount];
1604  auto const bridgeSpec = ctx.tx[sfXChainBridge];
1605 
1606  STXChainBridge::ChainType const chainType =
1607  STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor());
1608 
1609  if (!ctx.view.read(keylet::bridge(bridgeSpec, chainType)))
1610  {
1611  return tecNO_ENTRY;
1612  }
1613 
1614  return tesSUCCESS;
1615 }
1616 
1617 TER
1619 {
1620  auto const account = ctx_.tx[sfAccount];
1621  auto const bridgeSpec = ctx_.tx[sfXChainBridge];
1622  auto const reward = ctx_.tx[~sfSignatureReward];
1623  auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
1624  bool const clearAccountCreate =
1626 
1627  auto const sleAcct = ctx_.view().peek(keylet::account(account));
1628  if (!sleAcct)
1629  return tecINTERNAL;
1630 
1631  STXChainBridge::ChainType const chainType =
1632  STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor());
1633 
1634  auto const sleBridge =
1635  ctx_.view().peek(keylet::bridge(bridgeSpec, chainType));
1636  if (!sleBridge)
1637  return tecINTERNAL;
1638 
1639  if (reward)
1640  (*sleBridge)[sfSignatureReward] = *reward;
1641  if (minAccountCreate)
1642  {
1643  (*sleBridge)[sfMinAccountCreateAmount] = *minAccountCreate;
1644  }
1645  if (clearAccountCreate &&
1646  sleBridge->isFieldPresent(sfMinAccountCreateAmount))
1647  {
1648  sleBridge->makeFieldAbsent(sfMinAccountCreateAmount);
1649  }
1650  ctx_.view().update(sleBridge);
1651 
1652  return tesSUCCESS;
1653 }
1654 
1655 //------------------------------------------------------------------------------
1656 
1657 NotTEC
1659 {
1660  if (!ctx.rules.enabled(featureXChainBridge))
1661  return temDISABLED;
1662 
1663  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
1664  return ret;
1665 
1666  if (ctx.tx.getFlags() & tfUniversalMask)
1667  return temINVALID_FLAG;
1668 
1669  STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge];
1670  auto const amount = ctx.tx[sfAmount];
1671 
1672  if (amount.signum() <= 0 ||
1673  (amount.issue() != bridgeSpec.lockingChainIssue() &&
1674  amount.issue() != bridgeSpec.issuingChainIssue()))
1675  {
1676  return temBAD_AMOUNT;
1677  }
1678 
1679  return preflight2(ctx);
1680 }
1681 
1682 TER
1684 {
1685  AccountID const account = ctx.tx[sfAccount];
1686  STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge];
1687  STAmount const& thisChainAmount = ctx.tx[sfAmount];
1688  auto const claimID = ctx.tx[sfXChainClaimID];
1689 
1690  auto const sleBridge = readBridge(ctx.view, bridgeSpec);
1691  if (!sleBridge)
1692  {
1693  return tecNO_ENTRY;
1694  }
1695 
1696  if (!ctx.view.read(keylet::account(ctx.tx[sfDestination])))
1697  {
1698  return tecNO_DST;
1699  }
1700 
1701  auto const thisDoor = (*sleBridge)[sfAccount];
1702  bool isLockingChain = false;
1703  {
1704  if (thisDoor == bridgeSpec.lockingChainDoor())
1705  isLockingChain = true;
1706  else if (thisDoor == bridgeSpec.issuingChainDoor())
1707  isLockingChain = false;
1708  else
1709  return tecINTERNAL;
1710  }
1711 
1712  {
1713  // Check that the amount specified matches the expected issue
1714 
1715  if (isLockingChain)
1716  {
1717  if (bridgeSpec.lockingChainIssue() != thisChainAmount.issue())
1719  }
1720  else
1721  {
1722  if (bridgeSpec.issuingChainIssue() != thisChainAmount.issue())
1724  }
1725  }
1726 
1727  if (isXRP(bridgeSpec.lockingChainIssue()) !=
1728  isXRP(bridgeSpec.issuingChainIssue()))
1729  {
1730  // Should have been caught when creating the bridge
1731  // Detect here so `otherChainAmount` doesn't switch from IOU -> XRP
1732  // and the numeric issues that need to be addressed with that.
1733  return tecINTERNAL;
1734  }
1735 
1736  auto const otherChainAmount = [&]() -> STAmount {
1737  STAmount r(thisChainAmount);
1738  if (isLockingChain)
1739  r.setIssue(bridgeSpec.issuingChainIssue());
1740  else
1741  r.setIssue(bridgeSpec.lockingChainIssue());
1742  return r;
1743  }();
1744 
1745  auto const sleClaimID =
1746  ctx.view.read(keylet::xChainClaimID(bridgeSpec, claimID));
1747  {
1748  // Check that the sequence number is owned by the sender of this
1749  // transaction
1750  if (!sleClaimID)
1751  {
1752  return tecXCHAIN_NO_CLAIM_ID;
1753  }
1754 
1755  if ((*sleClaimID)[sfAccount] != account)
1756  {
1757  // Sequence number isn't owned by the sender of this transaction
1758  return tecXCHAIN_BAD_CLAIM_ID;
1759  }
1760  }
1761 
1762  // quorum is checked in `doApply`
1763  return tesSUCCESS;
1764 }
1765 
1766 TER
1768 {
1769  PaymentSandbox psb(&ctx_.view());
1770 
1771  AccountID const account = ctx_.tx[sfAccount];
1772  auto const dst = ctx_.tx[sfDestination];
1773  STXChainBridge const bridgeSpec = ctx_.tx[sfXChainBridge];
1774  STAmount const& thisChainAmount = ctx_.tx[sfAmount];
1775  auto const claimID = ctx_.tx[sfXChainClaimID];
1776  auto const claimIDKeylet = keylet::xChainClaimID(bridgeSpec, claimID);
1777 
1778  struct ScopeResult
1779  {
1780  std::vector<AccountID> rewardAccounts;
1781  AccountID rewardPoolSrc;
1782  STAmount sendingAmount;
1783  STXChainBridge::ChainType srcChain;
1784  STAmount signatureReward;
1785  };
1786 
1787  auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
1788  // This lambda is ugly - admittedly. The purpose of this lambda is to
1789  // limit the scope of sles so they don't overlap with
1790  // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
1791  // views, it's important that the sle's lifetime doesn't overlap.
1792 
1793  auto const sleAcct = psb.peek(keylet::account(account));
1794  auto const sleBridge = peekBridge(psb, bridgeSpec);
1795  auto const sleClaimID = psb.peek(claimIDKeylet);
1796 
1797  if (!(sleBridge && sleClaimID && sleAcct))
1798  return Unexpected(tecINTERNAL);
1799 
1800  AccountID const thisDoor = (*sleBridge)[sfAccount];
1801 
1803  {
1804  if (thisDoor == bridgeSpec.lockingChainDoor())
1806  else if (thisDoor == bridgeSpec.issuingChainDoor())
1808  else
1809  return Unexpected(tecINTERNAL);
1810  }
1811  STXChainBridge::ChainType const srcChain =
1812  STXChainBridge::otherChain(dstChain);
1813 
1814  auto const sendingAmount = [&]() -> STAmount {
1815  STAmount r(thisChainAmount);
1816  r.setIssue(bridgeSpec.issue(srcChain));
1817  return r;
1818  }();
1819 
1820  auto const [signersList, quorum, slTer] =
1821  getSignersListAndQuorum(ctx_.view(), *sleBridge, ctx_.journal);
1822 
1823  if (!isTesSuccess(slTer))
1824  return Unexpected(slTer);
1825 
1826  XChainClaimAttestations curAtts{
1827  sleClaimID->getFieldArray(sfXChainClaimAttestations)};
1828 
1829  auto const claimR = onClaim(
1830  curAtts,
1831  psb,
1832  sendingAmount,
1833  /*wasLockingChainSend*/ srcChain ==
1835  quorum,
1836  signersList,
1837  ctx_.journal);
1838  if (!claimR.has_value())
1839  return Unexpected(claimR.error());
1840 
1841  return ScopeResult{
1842  claimR.value(),
1843  (*sleClaimID)[sfAccount],
1844  sendingAmount,
1845  srcChain,
1846  (*sleClaimID)[sfSignatureReward],
1847  };
1848  }();
1849 
1850  if (!scopeResult.has_value())
1851  return scopeResult.error();
1852 
1853  auto const& [rewardAccounts, rewardPoolSrc, sendingAmount, srcChain, signatureReward] =
1854  scopeResult.value();
1856 
1857  auto const r = finalizeClaimHelper(
1858  psb,
1859  bridgeSpec,
1860  dst,
1861  dstTag,
1862  /*claimOwner*/ account,
1863  sendingAmount,
1864  rewardPoolSrc,
1865  signatureReward,
1866  rewardAccounts,
1867  srcChain,
1868  claimIDKeylet,
1869  OnTransferFail::keepClaim,
1870  DepositAuthPolicy::dstCanBypass,
1871  ctx_.journal);
1872  if (!r.isTesSuccess())
1873  return r.ter();
1874 
1875  psb.apply(ctx_.rawView());
1876 
1877  return tesSUCCESS;
1878 }
1879 
1880 //------------------------------------------------------------------------------
1881 
1884 {
1885  auto const maxSpend = [&] {
1886  auto const amount = ctx.tx[sfAmount];
1887  if (amount.native() && amount.signum() > 0)
1888  return amount.xrp();
1889  return XRPAmount{beast::zero};
1890  }();
1891 
1892  return TxConsequences{ctx.tx, maxSpend};
1893 }
1894 
1895 NotTEC
1897 {
1898  if (!ctx.rules.enabled(featureXChainBridge))
1899  return temDISABLED;
1900 
1901  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
1902  return ret;
1903 
1904  if (ctx.tx.getFlags() & tfUniversalMask)
1905  return temINVALID_FLAG;
1906 
1907  auto const amount = ctx.tx[sfAmount];
1908  auto const bridgeSpec = ctx.tx[sfXChainBridge];
1909 
1910  if (amount.signum() <= 0 || !isLegalNet(amount))
1911  return temBAD_AMOUNT;
1912 
1913  if (amount.issue() != bridgeSpec.lockingChainIssue() &&
1914  amount.issue() != bridgeSpec.issuingChainIssue())
1915  return temBAD_ISSUER;
1916 
1917  return preflight2(ctx);
1918 }
1919 
1920 TER
1922 {
1923  auto const bridgeSpec = ctx.tx[sfXChainBridge];
1924  auto const amount = ctx.tx[sfAmount];
1925 
1926  auto const sleBridge = readBridge(ctx.view, bridgeSpec);
1927  if (!sleBridge)
1928  {
1929  return tecNO_ENTRY;
1930  }
1931 
1932  AccountID const thisDoor = (*sleBridge)[sfAccount];
1933  AccountID const account = ctx.tx[sfAccount];
1934 
1935  if (thisDoor == account)
1936  {
1937  // Door account can't lock funds onto itself
1938  return tecXCHAIN_SELF_COMMIT;
1939  }
1940 
1941  bool isLockingChain = false;
1942  {
1943  if (thisDoor == bridgeSpec.lockingChainDoor())
1944  isLockingChain = true;
1945  else if (thisDoor == bridgeSpec.issuingChainDoor())
1946  isLockingChain = false;
1947  else
1948  return tecINTERNAL;
1949  }
1950 
1951  if (isLockingChain)
1952  {
1953  if (bridgeSpec.lockingChainIssue() != ctx.tx[sfAmount].issue())
1955  }
1956  else
1957  {
1958  if (bridgeSpec.issuingChainIssue() != ctx.tx[sfAmount].issue())
1960  }
1961 
1962  return tesSUCCESS;
1963 }
1964 
1965 TER
1967 {
1968  PaymentSandbox psb(&ctx_.view());
1969 
1970  auto const account = ctx_.tx[sfAccount];
1971  auto const amount = ctx_.tx[sfAmount];
1972  auto const bridgeSpec = ctx_.tx[sfXChainBridge];
1973 
1974  if (!psb.read(keylet::account(account)))
1975  return tecINTERNAL;
1976 
1977  auto const sleBridge = readBridge(psb, bridgeSpec);
1978  if (!sleBridge)
1979  return tecINTERNAL;
1980 
1981  auto const dst = (*sleBridge)[sfAccount];
1982 
1983  // Support dipping into reserves to pay the fee
1984  TransferHelperSubmittingAccountInfo submittingAccountInfo{
1986 
1987  auto const thTer = transferHelper(
1988  psb,
1989  account,
1990  dst,
1991  /*dstTag*/ std::nullopt,
1992  /*claimOwner*/ std::nullopt,
1993  amount,
1994  CanCreateDstPolicy::no,
1995  DepositAuthPolicy::normal,
1996  submittingAccountInfo,
1997  ctx_.journal);
1998 
1999  if (!isTesSuccess(thTer))
2000  return thTer;
2001 
2002  psb.apply(ctx_.rawView());
2003 
2004  return tesSUCCESS;
2005 }
2006 
2007 //------------------------------------------------------------------------------
2008 
2009 NotTEC
2011 {
2012  if (!ctx.rules.enabled(featureXChainBridge))
2013  return temDISABLED;
2014 
2015  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
2016  return ret;
2017 
2018  if (ctx.tx.getFlags() & tfUniversalMask)
2019  return temINVALID_FLAG;
2020 
2021  auto const reward = ctx.tx[sfSignatureReward];
2022 
2023  if (!isXRP(reward) || reward.signum() < 0 || !isLegalNet(reward))
2025 
2026  return preflight2(ctx);
2027 }
2028 
2029 TER
2031 {
2032  auto const account = ctx.tx[sfAccount];
2033  auto const bridgeSpec = ctx.tx[sfXChainBridge];
2034  auto const sleBridge = readBridge(ctx.view, bridgeSpec);
2035 
2036  if (!sleBridge)
2037  {
2038  return tecNO_ENTRY;
2039  }
2040 
2041  // Check that the reward matches
2042  auto const reward = ctx.tx[sfSignatureReward];
2043 
2044  if (reward != (*sleBridge)[sfSignatureReward])
2045  {
2047  }
2048 
2049  {
2050  // Check reserve
2051  auto const sleAcc = ctx.view.read(keylet::account(account));
2052  if (!sleAcc)
2053  return terNO_ACCOUNT;
2054 
2055  auto const balance = (*sleAcc)[sfBalance];
2056  auto const reserve =
2057  ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1);
2058 
2059  if (balance < reserve)
2060  return tecINSUFFICIENT_RESERVE;
2061  }
2062 
2063  return tesSUCCESS;
2064 }
2065 
2066 TER
2068 {
2069  auto const account = ctx_.tx[sfAccount];
2070  auto const bridgeSpec = ctx_.tx[sfXChainBridge];
2071  auto const reward = ctx_.tx[sfSignatureReward];
2072  auto const otherChainSrc = ctx_.tx[sfOtherChainSource];
2073 
2074  auto const sleAcct = ctx_.view().peek(keylet::account(account));
2075  if (!sleAcct)
2076  return tecINTERNAL;
2077 
2078  auto const sleBridge = peekBridge(ctx_.view(), bridgeSpec);
2079  if (!sleBridge)
2080  return tecINTERNAL;
2081 
2082  std::uint32_t const claimID = (*sleBridge)[sfXChainClaimID] + 1;
2083  if (claimID == 0)
2084  return tecINTERNAL; // overflow
2085 
2086  (*sleBridge)[sfXChainClaimID] = claimID;
2087 
2088  Keylet const claimIDKeylet = keylet::xChainClaimID(bridgeSpec, claimID);
2089  if (ctx_.view().exists(claimIDKeylet))
2090  return tecINTERNAL; // already checked out!?!
2091 
2092  auto const sleClaimID = std::make_shared<SLE>(claimIDKeylet);
2093 
2094  (*sleClaimID)[sfAccount] = account;
2095  (*sleClaimID)[sfXChainBridge] = bridgeSpec;
2096  (*sleClaimID)[sfXChainClaimID] = claimID;
2097  (*sleClaimID)[sfOtherChainSource] = otherChainSrc;
2098  (*sleClaimID)[sfSignatureReward] = reward;
2099  sleClaimID->setFieldArray(
2101 
2102  // Add to owner directory
2103  {
2104  auto const page = ctx_.view().dirInsert(
2105  keylet::ownerDir(account),
2106  claimIDKeylet,
2107  describeOwnerDir(account));
2108  if (!page)
2109  return tecDIR_FULL;
2110  (*sleClaimID)[sfOwnerNode] = *page;
2111  }
2112 
2113  adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal);
2114 
2115  ctx_.view().insert(sleClaimID);
2116  ctx_.view().update(sleBridge);
2117  ctx_.view().update(sleAcct);
2118 
2119  return tesSUCCESS;
2120 }
2121 
2122 //------------------------------------------------------------------------------
2123 
2124 NotTEC
2126 {
2127  return attestationPreflight<Attestations::AttestationClaim>(ctx);
2128 }
2129 
2130 TER
2132 {
2133  return attestationPreclaim<Attestations::AttestationClaim>(ctx);
2134 }
2135 
2136 TER
2138 {
2139  return attestationDoApply<Attestations::AttestationClaim>(ctx_);
2140 }
2141 
2142 //------------------------------------------------------------------------------
2143 
2144 NotTEC
2146 {
2147  return attestationPreflight<Attestations::AttestationCreateAccount>(ctx);
2148 }
2149 
2150 TER
2152 {
2153  return attestationPreclaim<Attestations::AttestationCreateAccount>(ctx);
2154 }
2155 
2156 TER
2158 {
2159  return attestationDoApply<Attestations::AttestationCreateAccount>(ctx_);
2160 }
2161 
2162 //------------------------------------------------------------------------------
2163 
2164 NotTEC
2166 {
2167  if (!ctx.rules.enabled(featureXChainBridge))
2168  return temDISABLED;
2169 
2170  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
2171  return ret;
2172 
2173  if (ctx.tx.getFlags() & tfUniversalMask)
2174  return temINVALID_FLAG;
2175 
2176  auto const amount = ctx.tx[sfAmount];
2177 
2178  if (amount.signum() <= 0 || !amount.native())
2179  return temBAD_AMOUNT;
2180 
2181  auto const reward = ctx.tx[sfSignatureReward];
2182  if (reward.signum() < 0 || !reward.native())
2183  return temBAD_AMOUNT;
2184 
2185  if (reward.issue() != amount.issue())
2186  return temBAD_AMOUNT;
2187 
2188  return preflight2(ctx);
2189 }
2190 
2191 TER
2193 {
2194  STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge];
2195  STAmount const amount = ctx.tx[sfAmount];
2196  STAmount const reward = ctx.tx[sfSignatureReward];
2197 
2198  auto const sleBridge = readBridge(ctx.view, bridgeSpec);
2199  if (!sleBridge)
2200  {
2201  return tecNO_ENTRY;
2202  }
2203 
2204  if (reward != (*sleBridge)[sfSignatureReward])
2205  {
2207  }
2208 
2209  std::optional<STAmount> const minCreateAmount =
2210  (*sleBridge)[~sfMinAccountCreateAmount];
2211 
2212  if (!minCreateAmount)
2214 
2215  if (amount < *minCreateAmount)
2217 
2218  if (minCreateAmount->issue() != amount.issue())
2220 
2221  AccountID const thisDoor = (*sleBridge)[sfAccount];
2222  AccountID const account = ctx.tx[sfAccount];
2223  if (thisDoor == account)
2224  {
2225  // Door account can't lock funds onto itself
2226  return tecXCHAIN_SELF_COMMIT;
2227  }
2228 
2230  {
2231  if (thisDoor == bridgeSpec.lockingChainDoor())
2233  else if (thisDoor == bridgeSpec.issuingChainDoor())
2235  else
2236  return tecINTERNAL;
2237  }
2238  STXChainBridge::ChainType const dstChain =
2239  STXChainBridge::otherChain(srcChain);
2240 
2241  if (bridgeSpec.issue(srcChain) != ctx.tx[sfAmount].issue())
2243 
2244  if (!isXRP(bridgeSpec.issue(dstChain)))
2246 
2247  return tesSUCCESS;
2248 }
2249 
2250 TER
2252 {
2253  PaymentSandbox psb(&ctx_.view());
2254 
2255  AccountID const account = ctx_.tx[sfAccount];
2256  STAmount const amount = ctx_.tx[sfAmount];
2257  STAmount const reward = ctx_.tx[sfSignatureReward];
2258  STXChainBridge const bridge = ctx_.tx[sfXChainBridge];
2259 
2260  auto const sle = psb.peek(keylet::account(account));
2261  if (!sle)
2262  return tecINTERNAL;
2263 
2264  auto const sleBridge = peekBridge(psb, bridge);
2265  if (!sleBridge)
2266  return tecINTERNAL;
2267 
2268  auto const dst = (*sleBridge)[sfAccount];
2269 
2270  // Support dipping into reserves to pay the fee
2271  TransferHelperSubmittingAccountInfo submittingAccountInfo{
2273  STAmount const toTransfer = amount + reward;
2274  auto const thTer = transferHelper(
2275  psb,
2276  account,
2277  dst,
2278  /*dstTag*/ std::nullopt,
2279  /*claimOwner*/ std::nullopt,
2280  toTransfer,
2281  CanCreateDstPolicy::yes,
2282  DepositAuthPolicy::normal,
2283  submittingAccountInfo,
2284  ctx_.journal);
2285 
2286  if (!isTesSuccess(thTer))
2287  return thTer;
2288 
2289  (*sleBridge)[sfXChainAccountCreateCount] =
2290  (*sleBridge)[sfXChainAccountCreateCount] + 1;
2291  psb.update(sleBridge);
2292 
2293  psb.apply(ctx_.rawView());
2294 
2295  return tesSUCCESS;
2296 }
2297 
2298 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:338
ripple::XChainClaim::doApply
TER doApply() override
Definition: XChainBridge.cpp:1767
ripple::temXCHAIN_BAD_PROOF
@ temXCHAIN_BAD_PROOF
Definition: TER.h:131
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:312
ripple::tecXCHAIN_ACCOUNT_CREATE_TOO_MANY
@ tecXCHAIN_ACCOUNT_CREATE_TOO_MANY
Definition: TER.h:329
ripple::sfOwnerCount
const SF_UINT32 sfOwnerCount
std::is_same_v
T is_same_v
ripple::STXChainBridge::otherChain
static ChainType otherChain(ChainType ct)
Definition: STXChainBridge.h:210
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:133
ripple::tecXCHAIN_BAD_CLAIM_ID
@ tecXCHAIN_BAD_CLAIM_ID
Definition: TER.h:319
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
ripple::STXChainBridge::srcChain
static ChainType srcChain(bool wasLockingChainSend)
Definition: STXChainBridge.h:218
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
std::shared_ptr
STL class.
ripple::PreclaimContext::view
ReadView const & view
Definition: Transactor.h:56
ripple::tecXCHAIN_PROOF_UNKNOWN_KEY
@ tecXCHAIN_PROOF_UNKNOWN_KEY
Definition: TER.h:321
ripple::sfOwnerNode
const SF_UINT64 sfOwnerNode
ripple::tecXCHAIN_CLAIM_NO_QUORUM
@ tecXCHAIN_CLAIM_NO_QUORUM
Definition: TER.h:320
ripple::STXChainBridge::ChainType
ChainType
Definition: STXChainBridge.h:42
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:308
ripple::ApplyView::peek
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
ripple::STXChainBridge::issue
Issue const & issue(ChainType ct) const
Definition: STXChainBridge.h:202
ripple::lsfDisableMaster
@ lsfDisableMaster
Definition: LedgerFormats.h:260
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:350
ripple::sfDestination
const SF_ACCOUNT sfDestination
ripple::describeOwnerDir
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition: View.cpp:748
ripple::SignerEntries::deserialize
static Expected< std::vector< SignerEntry >, NotTEC > deserialize(STObject const &obj, beast::Journal journal, std::string const &annotation)
Definition: SignerEntries.cpp:30
ripple::sfAmount
const SF_AMOUNT sfAmount
ripple::PaymentSandbox
A wrapper which makes credits unavailable to balances.
Definition: PaymentSandbox.h:112
ripple::isTesSuccess
bool isTesSuccess(TER x)
Definition: TER.h:637
unordered_set
std::vector::reserve
T reserve(T... args)
ripple::STXChainBridge::dstChain
static ChainType dstChain(bool wasLockingChainSend)
Definition: STXChainBridge.h:226
ripple::STXChainBridge::lockingChainIssue
Issue const & lockingChainIssue() const
Definition: STXChainBridge.h:170
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::sfRegularKey
const SF_ACCOUNT sfRegularKey
std::vector
STL class.
std::unordered_map::find
T find(T... args)
std::vector::size
T size(T... args)
ripple::SLE
STLedgerEntry SLE
Definition: Application.h:66
ripple::ReadView::fees
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
ripple::STXChainBridge::ChainType::issuing
@ issuing
ripple::tecXCHAIN_WRONG_CHAIN
@ tecXCHAIN_WRONG_CHAIN
Definition: TER.h:323
ripple::tecXCHAIN_SENDING_ACCOUNT_MISMATCH
@ tecXCHAIN_SENDING_ACCOUNT_MISMATCH
Definition: TER.h:326
ripple::temXCHAIN_EQUAL_DOOR_ACCOUNTS
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition: TER.h:130
ripple::Unexpected
Unexpected(E(&)[N]) -> Unexpected< E const * >
ripple::XChainCreateClaimID::doApply
TER doApply() override
Definition: XChainBridge.cpp:2067
ripple::tecDST_TAG_NEEDED
@ tecDST_TAG_NEEDED
Definition: TER.h:290
ripple::detail::ApplyViewBase::update
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
Definition: ApplyViewBase.cpp:146
ripple::XChainCommit::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:1921
ripple::STXChainBridge::ChainType::locking
@ locking
ripple::sfOtherChainSource
const SF_ACCOUNT sfOtherChainSource
ripple::tecXCHAIN_NO_CLAIM_ID
@ tecXCHAIN_NO_CLAIM_ID
Definition: TER.h:318
Json::check
void check(bool condition, std::string const &message)
Definition: json/Writer.h:252
std::distance
T distance(T... args)
ripple::ApplyView::update
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
ripple::ApplyContext::journal
const beast::Journal journal
Definition: ApplyContext.h:51
std::tuple
ripple::keylet::xChainClaimID
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition: Indexes.cpp:414
ripple::isLegalNet
bool isLegalNet(STAmount const &value)
Definition: STAmount.h:449
ripple::ApplyContext::rawView
RawView & rawView()
Definition: ApplyContext.h:67
ripple::test::ownerCount
std::uint32_t ownerCount(test::jtx::Env const &env, test::jtx::Account const &acct)
Definition: DID_test.cpp:35
ripple::sfXChainAccountClaimCount
const SF_UINT64 sfXChainAccountClaimCount
ripple::temBAD_ISSUER
@ temBAD_ISSUER
Definition: TER.h:92
ripple::lsfAllowTrustLineClawback
@ lsfAllowTrustLineClawback
Definition: LedgerFormats.h:278
ripple::XChainAddClaimAttestation::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:2131
ripple::isTecClaim
bool isTecClaim(TER x)
Definition: TER.h:643
ripple::temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
@ temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
Definition: TER.h:135
ripple::generateKeyPair
std::pair< PublicKey, SecretKey > generateKeyPair(KeyType type, Seed const &seed)
Generate a key pair deterministically.
Definition: SecretKey.cpp:351
ripple::preflight1
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:81
ripple::lsfDepositAuth
@ lsfDepositAuth
Definition: LedgerFormats.h:265
ripple::sfXChainClaimID
const SF_UINT64 sfXChainClaimID
ripple::temXCHAIN_BRIDGE_BAD_ISSUES
@ temXCHAIN_BRIDGE_BAD_ISSUES
Definition: TER.h:132
std::unordered_map::contains
T contains(T... args)
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
ripple::sfSignerQuorum
const SF_UINT32 sfSignerQuorum
std::vector::push_back
T push_back(T... args)
ripple::divide
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:86
ripple::isTerRetry
bool isTerRetry(TER x)
Definition: TER.h:631
ripple::tecNO_DST_INSUF_XRP
@ tecNO_DST_INSUF_XRP
Definition: TER.h:272
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint< 160, detail::AccountIDTag >
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::XChainCreateBridge::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:1367
ripple::tefBAD_LEDGER
@ tefBAD_LEDGER
Definition: TER.h:164
ripple::adjustOwnerCount
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition: View.cpp:730
ripple::XChainCreateAccountCommit::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:2192
ripple::STXChainBridge::issuingChainIssue
Issue const & issuingChainIssue() const
Definition: STXChainBridge.h:182
ripple::Expected
Definition: Expected.h:132
ripple::sfSignatureReward
const SF_AMOUNT sfSignatureReward
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:142
ripple::XChainCreateAccountCommit::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:2165
ripple::sfXChainClaimAttestations
const SField sfXChainClaimAttestations
ripple::tecDUPLICATE
@ tecDUPLICATE
Definition: TER.h:296
ripple::keylet::bridge
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition: Indexes.cpp:401
ripple::TERSubset< CanCvtToTER >
ripple::keylet::page
Keylet page(uint256 const &key, std::uint64_t index) noexcept
A page in a directory.
Definition: Indexes.cpp:318
ripple::isTefFailure
bool isTefFailure(TER x)
Definition: TER.h:625
ripple::XChainCreateBridge::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:1451
ripple::calcAccountID
AccountID calcAccountID(PublicKey const &pk)
Definition: AccountID.cpp:158
ripple::XChainCreateClaimID::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:2010
ripple::STArray
Definition: STArray.h:28
ripple::flow
path::RippleCalc::Output flow(PaymentSandbox &sb, STAmount const &deliver, AccountID const &src, AccountID const &dst, STPathSet const &paths, bool defaultPaths, bool partialPayment, bool ownerPaysTransferFee, bool offerCrossing, std::optional< Quality > const &limitQuality, std::optional< STAmount > const &sendMax, beast::Journal j, path::detail::FlowDebugInfo *flowDebugInfo)
Make a payment from the src account to the dst account.
Definition: Flow.cpp:59
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:608
ripple::STAmount
Definition: STAmount.h:46
ripple::tecUNFUNDED_PAYMENT
@ tecUNFUNDED_PAYMENT
Definition: TER.h:266
ripple::tecXCHAIN_ACCOUNT_CREATE_PAST
@ tecXCHAIN_ACCOUNT_CREATE_PAST
Definition: TER.h:328
ripple::ReadView::exists
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:291
ripple::STObject::getFlags
std::uint32_t getFlags() const
Definition: STObject.cpp:481
ripple::XChainClaim::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:1683
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
ripple::tecXCHAIN_PAYMENT_FAILED
@ tecXCHAIN_PAYMENT_FAILED
Definition: TER.h:330
ripple::temBAD_AMOUNT
@ temBAD_AMOUNT
Definition: TER.h:88
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::sfAttestationSignerAccount
const SF_ACCOUNT sfAttestationSignerAccount
std::uint32_t
ripple::tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE
@ tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE
Definition: TER.h:322
ripple::STAmount::setIssue
void setIssue(Issue const &issue)
Set the Issue for this amount and update mIsNative.
Definition: STAmount.cpp:490
ripple::tecXCHAIN_BAD_TRANSFER_ISSUE
@ tecXCHAIN_BAD_TRANSFER_ISSUE
Definition: TER.h:317
ripple::XChainClaimAttestations
Definition: XChainAttestations.h:488
ripple::featureXChainBridge
const uint256 featureXChainBridge
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::BridgeModify::doApply
TER doApply() override
Definition: XChainBridge.cpp:1618
ripple::ApplyContext::view
ApplyView & view()
Definition: ApplyContext.h:54
ripple::STXChainBridge::lockingChainDoor
AccountID const & lockingChainDoor() const
Definition: STXChainBridge.h:164
ripple::PreclaimContext::tx
STTx const & tx
Definition: Transactor.h:58
ripple::STXChainBridge::issuingChainDoor
AccountID const & issuingChainDoor() const
Definition: STXChainBridge.h:176
ripple::STXChainBridge
Definition: STXChainBridge.h:32
ripple::lsfRequireDestTag
@ lsfRequireDestTag
Definition: LedgerFormats.h:255
ripple::tecDIR_FULL
@ tecDIR_FULL
Definition: TER.h:268
ripple::KeyType::secp256k1
@ secp256k1
ripple::terNO_ACCOUNT
@ terNO_ACCOUNT
Definition: TER.h:210
ripple::PreclaimContext
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:52
ripple::xbridgeMaxAccountCreateClaims
constexpr size_t xbridgeMaxAccountCreateClaims
Definition: XChainBridge.h:29
ripple::generateSeed
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition: Seed.cpp:69
ripple::sfMinAccountCreateAmount
const SF_AMOUNT sfMinAccountCreateAmount
ripple::ApplyView::insert
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::XChainCreateClaimID::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:2030
ripple::tfClearAccountCreateAmount
constexpr std::uint32_t tfClearAccountCreateAmount
Definition: TxFlags.h:185
ripple::XChainCreateAccountCommit::doApply
TER doApply() override
Definition: XChainBridge.cpp:2251
ripple::tecXCHAIN_CREATE_ACCOUNT_DISABLED
@ tecXCHAIN_CREATE_ACCOUNT_DISABLED
Definition: TER.h:333
ripple::ltBRIDGE
@ ltBRIDGE
The ledger object which lists details about sidechains.
Definition: LedgerFormats.h:99
ripple::tecXCHAIN_SELF_COMMIT
@ tecXCHAIN_SELF_COMMIT
Definition: TER.h:331
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:113
ripple::PaymentSandbox::apply
void apply(RawView &to)
Apply changes to base view.
Definition: PaymentSandbox.cpp:254
ripple::Fees::accountReserve
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Definition: protocol/Fees.h:49
ripple::XChainAddAccountCreateAttestation::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:2151
ripple::tecXCHAIN_REWARD_MISMATCH
@ tecXCHAIN_REWARD_MISMATCH
Definition: TER.h:324
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::XChainCommit::makeTxConsequences
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Definition: XChainBridge.cpp:1883
ripple::tecNO_ISSUER
@ tecNO_ISSUER
Definition: TER.h:280
ripple::sfDestinationTag
const SF_UINT32 sfDestinationTag
ripple::XChainCommit::doApply
TER doApply() override
Definition: XChainBridge.cpp:1966
ripple::Transactor::mPriorBalance
XRPAmount mPriorBalance
Definition: Transactor.h:92
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:286
ripple::Transactor::mSourceBalance
XRPAmount mSourceBalance
Definition: Transactor.h:93
ripple::XChainAddClaimAttestation::doApply
TER doApply() override
Definition: XChainBridge.cpp:2137
ripple::sfXChainAccountCreateCount
const SF_UINT64 sfXChainAccountCreateCount
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::XChainCommit::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:1896
ripple::XChainCreateBridge::doApply
TER doApply() override
Definition: XChainBridge.cpp:1502
ripple::XChainAddAccountCreateAttestation::doApply
TER doApply() override
Definition: XChainBridge.cpp:2157
ripple::keylet::xChainCreateAccountClaimID
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition: Indexes.cpp:428
ripple::tecINSUFFICIENT_RESERVE
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:288
std::vector::empty
T empty(T... args)
ripple::sfXChainCreateAccountAttestations
const SField sfXChainCreateAccountAttestations
ripple::Transactor::ctx_
ApplyContext & ctx_
Definition: Transactor.h:88
ripple::AttestationMatch
AttestationMatch
Definition: XChainAttestations.h:258
std::optional
ripple::XChainAddClaimAttestation::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:2125
ripple::BridgeModify::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: XChainBridge.cpp:1601
ripple::temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
@ temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
Definition: TER.h:134
ripple::detail::ApplyViewBase::read
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition: ApplyViewBase.cpp:71
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::detail::ApplyViewBase::peek
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Definition: ApplyViewBase.cpp:128
ripple::tecNO_ENTRY
@ tecNO_ENTRY
Definition: TER.h:287
std::unordered_map::end
T end(T... args)
ripple::tfBridgeModifyMask
constexpr std::uint32_t tfBridgeModifyMask
Definition: TxFlags.h:186
ripple::temMALFORMED
@ temMALFORMED
Definition: TER.h:86
ripple::PreflightContext::tx
STTx const & tx
Definition: Transactor.h:35
std::numeric_limits::max
T max(T... args)
ripple::PreflightContext
State information when preflighting a tx.
Definition: Transactor.h:31
ripple::keylet::signers
static Keylet signers(AccountID const &account, std::uint32_t page) noexcept
Definition: Indexes.cpp:277
ripple::XChainClaim::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:1658
ripple::ApplyView::dirInsert
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition: ApplyView.h:306
ripple::PreflightContext::rules
const Rules rules
Definition: Transactor.h:36
ripple::BridgeModify::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:1548
ripple::tfUniversalMask
constexpr std::uint32_t tfUniversalMask
Definition: TxFlags.h:60
unordered_map
ripple::keylet::depositPreauth
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:296
ripple::TxConsequences
Class describing the consequences to the account of applying a transaction if the transaction consume...
Definition: applySteps.h:45
ripple::sfXChainBridge
const SF_XCHAIN_BRIDGE sfXChainBridge
ripple::temXCHAIN_BRIDGE_NONDOOR_OWNER
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
Definition: TER.h:133
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
ripple::sfPublicKey
const SF_VL sfPublicKey
ripple::Transactor::account_
const AccountID account_
Definition: Transactor.h:91
ripple::tecXCHAIN_NO_SIGNERS_LIST
@ tecXCHAIN_NO_SIGNERS_LIST
Definition: TER.h:325
ripple::ApplyContext::tx
STTx const & tx
Definition: ApplyContext.h:48
ripple::AccountID
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:49
ripple::tecNO_DST
@ tecNO_DST
Definition: TER.h:271
ripple::XChainAddAccountCreateAttestation::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: XChainBridge.cpp:2145
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:568
ripple::tecXCHAIN_INSUFF_CREATE_AMOUNT
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition: TER.h:327