1#include <xrpl/tx/transactors/bridge/XChainBridge.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/beast/utility/Zero.h>
7#include <xrpl/beast/utility/instrumentation.h>
8#include <xrpl/core/ServiceRegistry.h>
9#include <xrpl/ledger/ApplyView.h>
10#include <xrpl/ledger/PaymentSandbox.h>
11#include <xrpl/ledger/RawView.h>
12#include <xrpl/ledger/ReadView.h>
13#include <xrpl/ledger/helpers/AccountRootHelpers.h>
14#include <xrpl/ledger/helpers/DirectoryHelpers.h>
15#include <xrpl/protocol/AccountID.h>
16#include <xrpl/protocol/Feature.h>
17#include <xrpl/protocol/Indexes.h>
18#include <xrpl/protocol/Issue.h>
19#include <xrpl/protocol/KeyType.h>
20#include <xrpl/protocol/Keylet.h>
21#include <xrpl/protocol/LedgerFormats.h>
22#include <xrpl/protocol/PublicKey.h>
23#include <xrpl/protocol/SField.h>
24#include <xrpl/protocol/STAmount.h>
25#include <xrpl/protocol/STLedgerEntry.h>
26#include <xrpl/protocol/STObject.h>
27#include <xrpl/protocol/STTx.h>
28#include <xrpl/protocol/STXChainBridge.h>
29#include <xrpl/protocol/SecretKey.h>
30#include <xrpl/protocol/Seed.h>
31#include <xrpl/protocol/TER.h>
32#include <xrpl/protocol/TxFlags.h>
33#include <xrpl/protocol/XChainAttestations.h>
34#include <xrpl/protocol/XRPAmount.h>
35#include <xrpl/tx/ApplyContext.h>
36#include <xrpl/tx/SignerEntries.h>
37#include <xrpl/tx/Transactor.h>
38#include <xrpl/tx/paths/Flow.h>
39#include <xrpl/tx/paths/detail/Steps.h>
114checkAttestationPublicKey(
116 std::unordered_map<AccountID, std::uint32_t>
const& signersList,
117 AccountID const& attestationSignerAccount,
121 if (!signersList.
contains(attestationSignerAccount))
128 if (
auto const sleAttestationSigningAccount =
131 if (accountFromPK == attestationSignerAccount)
134 if (sleAttestationSigningAccount->isFlag(lsfDisableMaster))
136 JLOG(j.
trace()) <<
"Attempt to add an attestation with "
137 "disabled master key.";
144 if (std::optional<AccountID>
const regularKey =
145 (*sleAttestationSigningAccount)[~sfRegularKey];
146 regularKey != accountFromPK)
150 JLOG(j.
trace()) <<
"Attempt to add an attestation with "
151 "account present and non-present regular key.";
155 JLOG(j.
trace()) <<
"Attempt to add an attestation with "
156 "account present and mismatched "
157 "regular key/public key.";
168 JLOG(j.
trace()) <<
"Attempt to add an attestation with non-existant account "
169 "and mismatched pk/account pair.";
188enum class CheckDst {
Check, Ignore };
189template <
class TAttestation>
190std::expected<std::vector<AccountID>,
TER>
194 typename TAttestation::MatchFields
const& toMatch,
196 std::uint32_t quorum,
197 std::unordered_map<AccountID, std::uint32_t>
const& signersList,
203 attestations.eraseIf([&](
auto const& a) {
204 return checkAttestationPublicKey(view, signersList, a.keyAccount, a.publicKey, j) !=
209 std::vector<AccountID> rewardAccounts;
210 rewardAccounts.
reserve(attestations.size());
211 std::uint32_t weight = 0;
212 for (
auto const& a : attestations)
214 auto const matchR = a.match(toMatch);
221 auto i = signersList.
find(a.keyAccount);
222 if (i == signersList.
end())
225 UNREACHABLE(
"xrpl::claimHelper : invalid inputs");
231 rewardAccounts.
push_back(a.rewardAccount);
234 if (weight >= quorum)
235 return rewardAccounts;
271struct OnNewAttestationResult
273 std::optional<std::vector<AccountID>> rewardAccounts;
279template <
class TAttestation>
280[[nodiscard]] OnNewAttestationResult
284 typename TAttestation::TSignedAttestation
const* attBegin,
285 typename TAttestation::TSignedAttestation
const* attEnd,
286 std::uint32_t quorum,
287 std::unordered_map<AccountID, std::uint32_t>
const& signersList,
290 bool changed =
false;
291 for (
auto att = attBegin; att != attEnd; ++att)
293 auto const ter = checkAttestationPublicKey(
294 view, signersList, att->attestationSignerAccount, att->publicKey, j);
304 auto const& claimSigningAccount = att->attestationSignerAccount;
306 attestations, [&](
auto const& a) {
return a.keyAccount == claimSigningAccount; });
307 i != attestations.end())
311 *i = TAttestation{*att};
316 attestations.emplaceBack(*att);
321 auto r = claimHelper(
324 typename TAttestation::MatchFields{*attBegin},
331 return {.rewardAccounts = std::nullopt, .changed = changed};
333 return {std::move(r.value()), changed};
339std::expected<std::vector<AccountID>,
TER>
344 bool wasLockingChainSend,
345 std::uint32_t quorum,
346 std::unordered_map<AccountID, std::uint32_t>
const& signersList,
350 sendingAmount, wasLockingChainSend, std::nullopt};
351 return claimHelper(attestations, view, toMatch, CheckDst::Ignore, quorum, signersList, j);
354enum class CanCreateDstPolicy {
No,
Yes };
356enum class DepositAuthPolicy {
Normal, DstCanBypass };
360struct TransferHelperSubmittingAccountInfo
363 STAmount preFeeBalance;
364 STAmount postFeeBalance;
394 std::optional<std::uint32_t>
const& dstTag,
395 std::optional<AccountID>
const& claimOwner,
397 CanCreateDstPolicy canCreate,
398 DepositAuthPolicy depositAuthPolicy,
399 std::optional<TransferHelperSubmittingAccountInfo>
const& submittingAccountInfo,
406 if (
auto sleDst = psb.read(dstK))
410 if (sleDst->isFlag(lsfRequireDestTag) && !dstTag)
416 bool const canBypassDepositAuth =
417 dst == claimOwner && depositAuthPolicy == DepositAuthPolicy::DstCanBypass;
419 if (!canBypassDepositAuth && sleDst->isFlag(lsfDepositAuth) &&
425 else if (!amt.native() || canCreate == CanCreateDstPolicy::No)
433 XRPL_ASSERT(sleSrc,
"xrpl::transferHelper : non-null source account");
438 auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
439 auto const reserve = psb.fees().accountReserve(ownerCount);
441 auto const availableBalance = [&]() ->
STAmount {
442 STAmount curBal = (*sleSrc)[sfBalance];
446 if (!submittingAccountInfo || submittingAccountInfo->account != src ||
447 submittingAccountInfo->postFeeBalance != curBal)
449 return submittingAccountInfo->preFeeBalance;
452 if (availableBalance < amt + reserve)
458 auto sleDst = psb.peek(dstK);
461 if (canCreate == CanCreateDstPolicy::No)
466 if (amt < psb.fees().reserve)
468 JLOG(j.
trace()) <<
"Insufficient payment to create account.";
474 sleDst->setAccountID(sfAccount, dst);
475 sleDst->setFieldU32(sfSequence, psb.seq());
480 (*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt;
481 (*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt;
488 auto const result =
flow(
513enum class OnTransferFail {
520struct FinalizeClaimHelperResult
523 std::optional<TER> mainFundsTer;
525 std::optional<TER> rewardTer;
527 std::optional<TER> rmSleTer;
548 return *mainFundsTer;
557 return *mainFundsTer;
594FinalizeClaimHelperResult
599 std::optional<std::uint32_t>
const& dstTag,
604 std::vector<AccountID>
const& rewardAccounts,
606 Keylet const& claimIDKeylet,
607 OnTransferFail onTransferFail,
608 DepositAuthPolicy depositAuthPolicy,
611 FinalizeClaimHelperResult result;
614 STAmount const thisChainAmount = [&] {
616 r.
setIssue(bridgeSpec.issue(dstChain));
619 auto const& thisDoor = bridgeSpec.door(dstChain);
633 result.mainFundsTer = transferHelper(
640 CanCreateDstPolicy::Yes,
645 if (!
isTesSuccess(*result.mainFundsTer) && onTransferFail == OnTransferFail::KeepClaim)
651 result.rewardTer = [&]() ->
TER {
652 if (rewardAccounts.
empty())
659 auto const roundMode = innerSb.rules().enabled(fixXChainRewardRounding)
665 return divide(rewardPool, den, rewardPool.asset());
668 for (
auto const& rewardAccount : rewardAccounts)
670 auto const thTer = transferHelper(
678 CanCreateDstPolicy::No,
679 DepositAuthPolicy::Normal,
687 distributed += share;
693 if (distributed > rewardPool)
700 (onTransferFail == OnTransferFail::KeepClaim || *result.rewardTer ==
tecINTERNAL))
710 innerSb.apply(outerSb);
714 if (
auto const sleClaimID = outerSb.peek(claimIDKeylet))
716 auto const cidOwner = (*sleClaimID)[sfAccount];
720 auto const page = (*sleClaimID)[sfOwnerNode];
721 if (!outerSb.dirRemove(
keylet::ownerDir(cidOwner), page, sleClaimID->key(),
true))
723 JLOG(j.
fatal()) <<
"Unable to delete xchain seq number from owner.";
729 outerSb.erase(sleClaimID);
747std::tuple<std::unordered_map<AccountID, std::uint32_t>, std::uint32_t,
TER>
748getSignersListAndQuorum(
ReadView const& view,
SLE const& sleBridge, beast::Journal j)
750 std::unordered_map<AccountID, std::uint32_t> r;
753 AccountID const thisDoor = sleBridge[sfAccount];
754 auto const sleDoor = [&] {
return view.read(
keylet::account(thisDoor)); }();
766 q = (*sleS)[sfSignerQuorum];
775 for (
auto const& as : *accountSigners)
777 r[as.account] = as.weight;
783template <
class R,
class F>
788 if (
auto r = getter(bridgeSpec, ct))
790 if ((*r)[sfXChainBridge] == bridgeSpec)
803 return readOrpeekBridge<SLE>(
813 return readOrpeekBridge<SLE const>(
822template <
class TIter>
824applyClaimAttestations(
831 std::unordered_map<AccountID, std::uint32_t>
const& signersList,
832 std::uint32_t quorum,
835 if (attBegin == attEnd)
844 OnNewAttestationResult newAttResult;
845 STAmount rewardAmount;
849 auto const scopeResult = [&]() -> std::expected<ScopeResult, TER> {
854 auto const sleClaimID = psb.peek(claimIDKeylet);
859 std::vector<Attestations::AttestationClaim> atts;
861 for (
auto att = attBegin; att != attEnd; ++att)
863 if (!signersList.
contains(att->attestationSignerAccount))
873 AccountID const otherChainSource = (*sleClaimID)[sfOtherChainSource];
874 if (attBegin->sendingAccount != otherChainSource)
885 if (attDstChain != dstChain)
893 auto const newAttResult = onNewAttestations(
897 &atts[0] + atts.
size(),
903 sleClaimID->setFieldArray(sfXChainClaimAttestations, curAtts.toSTArray());
904 psb.update(sleClaimID);
907 newAttResult, (*sleClaimID)[sfSignatureReward], (*sleClaimID)[sfAccount]};
910 if (!scopeResult.has_value())
911 return scopeResult.error();
913 auto const& [newAttResult, rewardAmount, cidOwner] = scopeResult.value();
914 auto const& [rewardAccounts, attListChanged] = newAttResult;
915 if (rewardAccounts && attBegin->dst)
917 auto const r = finalizeClaimHelper(
923 attBegin->sendingAmount,
929 OnTransferFail::KeepClaim,
930 DepositAuthPolicy::Normal,
933 auto const rTer = r.ter();
945template <
class TIter>
947applyCreateAccountAttestations(
957 std::unordered_map<AccountID, std::uint32_t>
const& signersList,
958 std::uint32_t quorum,
961 if (attBegin == attEnd)
966 auto const claimCountResult = [&]() -> std::expected<std::uint64_t, TER> {
967 auto const sleBridge = psb.peek(bridgeK);
971 return (*sleBridge)[sfXChainAccountClaimCount];
974 if (!claimCountResult.has_value())
975 return claimCountResult.error();
977 std::uint64_t
const claimCount = claimCountResult.value();
979 if (attBegin->createCount <= claimCount)
995 if (attDstChain != dstChain)
1001 auto const claimIDKeylet =
1006 OnNewAttestationResult newAttResult;
1008 XChainCreateAccountAttestations curAtts;
1011 auto const scopeResult = [&]() -> std::expected<ScopeResult, TER> {
1019 auto const sleClaimID = psb.peek(claimIDKeylet);
1020 bool createCID =
false;
1025 auto const sleDoor = psb.peek(doorK);
1030 auto const balance = (*sleDoor)[sfBalance];
1031 auto const reserve = psb.fees().accountReserve((*sleDoor)[sfOwnerCount] + 1);
1033 if (balance < reserve)
1037 std::vector<Attestations::AttestationCreateAccount> atts;
1039 for (
auto att = attBegin; att != attEnd; ++att)
1041 if (!signersList.
contains(att->attestationSignerAccount))
1054 sleClaimID->getFieldArray(sfXChainCreateAccountAttestations)};
1059 auto const newAttResult = onNewAttestations(
1063 &atts[0] + atts.
size(),
1074 sleClaimID->setFieldArray(sfXChainCreateAccountAttestations, curAtts.toSTArray());
1075 psb.update(sleClaimID);
1077 return ScopeResult{newAttResult, createCID, curAtts};
1080 if (!scopeResult.has_value())
1081 return scopeResult.error();
1083 auto const& [attResult, createCID, curAtts] = scopeResult.value();
1084 auto const& [rewardAccounts, attListChanged] = attResult;
1087 if (rewardAccounts && claimCount + 1 == attBegin->createCount)
1089 auto const r = finalizeClaimHelper(
1095 attBegin->sendingAmount,
1097 attBegin->rewardAmount,
1101 OnTransferFail::RemoveClaim,
1102 DepositAuthPolicy::Normal,
1105 auto const rTer = r.ter();
1114 auto const sleBridge = psb.peek(bridgeK);
1117 (*sleBridge)[sfXChainAccountClaimCount] = attBegin->createCount;
1118 psb.update(sleBridge);
1123 (*createdSleClaimID)[sfAccount] = doorAccount;
1124 (*createdSleClaimID)[sfXChainBridge] = bridgeSpec;
1125 (*createdSleClaimID)[sfXChainAccountCreateCount] = attBegin->createCount;
1126 createdSleClaimID->setFieldArray(sfXChainCreateAccountAttestations, curAtts.toSTArray());
1129 auto const page = psb.dirInsert(
1133 (*createdSleClaimID)[sfOwnerNode] = *
page;
1135 auto const sleDoor = psb.peek(doorK);
1141 psb.insert(createdSleClaimID);
1142 psb.update(sleDoor);
1150template <
class TAttestation>
1151std::optional<TAttestation>
1152toClaim(
STTx const& tx)
1162 return TAttestation(o);
1166 return std::nullopt;
1170template <
class TAttestation>
1177 auto const att = toClaim<TAttestation>(ctx.tx);
1182 if (!att->verify(bridgeSpec))
1184 if (!att->validAmounts())
1187 if (att->sendingAmount.signum() <= 0)
1190 if (att->sendingAmount.asset() != expectedIssue)
1196template <
class TAttestation>
1200 auto const att = toClaim<TAttestation>(ctx.tx);
1206 auto const sleBridge = readBridge(ctx.view, bridgeSpec);
1212 AccountID const attestationSignerAccount{ctx.tx[sfAttestationSignerAccount]};
1213 PublicKey const pk{ctx.tx[sfPublicKey]};
1216 auto const [signersList, quorum, slTer] = getSignersListAndQuorum(ctx.view, *sleBridge, ctx.j);
1221 return checkAttestationPublicKey(ctx.view, signersList, attestationSignerAccount, pk, ctx.j);
1224template <
class TAttestation>
1228 auto const att = toClaim<TAttestation>(ctx.tx);
1239 STXChainBridge::ChainType srcChain = STXChainBridge::ChainType::Locking;
1240 std::unordered_map<AccountID, std::uint32_t> signersList;
1241 std::uint32_t quorum{};
1246 auto const scopeResult = [&]() -> std::expected<ScopeResult, TER> {
1251 auto sleBridge = readBridge(ctx.view(), bridgeSpec);
1256 Keylet const bridgeK{ltBRIDGE, sleBridge->key()};
1257 AccountID const thisDoor = (*sleBridge)[sfAccount];
1261 if (thisDoor == bridgeSpec.lockingChainDoor())
1265 else if (thisDoor == bridgeSpec.issuingChainDoor())
1277 auto [signersList, quorum, slTer] =
1278 getSignersListAndQuorum(ctx.view(), *sleBridge, ctx.journal);
1283 return ScopeResult{srcChain, std::move(signersList), quorum, thisDoor, bridgeK};
1286 if (!scopeResult.has_value())
1287 return scopeResult.error();
1289 auto const& [srcChain, signersList, quorum, thisDoor, bridgeK] = scopeResult.value();
1297 return applyClaimAttestations(
1310 return applyCreateAccountAttestations(
1332 auto const account = ctx.
tx[sfAccount];
1333 auto const reward = ctx.
tx[sfSignatureReward];
1334 auto const minAccountCreate = ctx.
tx[~sfMinAccountCreateAmount];
1335 auto const bridgeSpec = ctx.
tx[sfXChainBridge];
1337 if (bridgeSpec.lockingChainDoor() == bridgeSpec.issuingChainDoor())
1342 if (bridgeSpec.lockingChainDoor() != account && bridgeSpec.issuingChainDoor() != account)
1347 if (
isXRP(bridgeSpec.lockingChainIssue()) !=
isXRP(bridgeSpec.issuingChainIssue()))
1354 if (!
isXRP(reward) || reward.signum() < 0)
1359 if (minAccountCreate &&
1360 ((!
isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1361 !
isXRP(bridgeSpec.lockingChainIssue()) || !
isXRP(bridgeSpec.issuingChainIssue())))
1366 if (
isXRP(bridgeSpec.issuingChainIssue()))
1373 if (bridgeSpec.issuingChainDoor() != kRootAccount)
1382 if (bridgeSpec.issuingChainDoor() != bridgeSpec.issuingChainIssue().account)
1388 if (bridgeSpec.lockingChainDoor() == bridgeSpec.lockingChainIssue().account)
1401 auto const account = ctx.
tx[sfAccount];
1402 auto const bridgeSpec = ctx.
tx[sfXChainBridge];
1418 if (!
isXRP(bridgeSpec.issue(chainType)))
1427 if (sleIssuer->isFlag(lsfAllowTrustLineClawback))
1437 auto const balance = (*sleAcc)[sfBalance];
1440 if (balance < reserve)
1450 auto const account =
ctx_.tx[sfAccount];
1451 auto const bridgeSpec =
ctx_.tx[sfXChainBridge];
1452 auto const reward =
ctx_.tx[sfSignatureReward];
1453 auto const minAccountCreate =
ctx_.tx[~sfMinAccountCreateAmount];
1465 (*sleBridge)[sfAccount] = account;
1466 (*sleBridge)[sfSignatureReward] = reward;
1467 if (minAccountCreate)
1468 (*sleBridge)[sfMinAccountCreateAmount] = *minAccountCreate;
1469 (*sleBridge)[sfXChainBridge] = bridgeSpec;
1470 (*sleBridge)[sfXChainClaimID] = 0;
1471 (*sleBridge)[sfXChainAccountCreateCount] = 0;
1472 (*sleBridge)[sfXChainAccountClaimCount] = 0;
1476 auto const page =
ctx_.view().dirInsert(
1480 (*sleBridge)[sfOwnerNode] = *page;
1485 ctx_.view().insert(sleBridge);
1486 ctx_.view().update(sleAcct);
1496 return tfXChainModifyBridgeMask;
1502 auto const account = ctx.
tx[sfAccount];
1503 auto const reward = ctx.
tx[~sfSignatureReward];
1504 auto const minAccountCreate = ctx.
tx[~sfMinAccountCreateAmount];
1505 auto const bridgeSpec = ctx.
tx[sfXChainBridge];
1506 bool const clearAccountCreate = ctx.
tx.
isFlag(tfClearAccountCreateAmount);
1508 if (!reward && !minAccountCreate && !clearAccountCreate)
1514 if (minAccountCreate && clearAccountCreate)
1520 if (bridgeSpec.lockingChainDoor() != account && bridgeSpec.issuingChainDoor() != account)
1525 if (reward && (!
isXRP(*reward) || reward->signum() < 0))
1530 if (minAccountCreate &&
1531 ((!
isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1532 !
isXRP(bridgeSpec.lockingChainIssue()) || !
isXRP(bridgeSpec.issuingChainIssue())))
1543 auto const account = ctx.
tx[sfAccount];
1544 auto const bridgeSpec = ctx.
tx[sfXChainBridge];
1560 auto const account =
ctx_.tx[sfAccount];
1561 auto const bridgeSpec =
ctx_.tx[sfXChainBridge];
1562 auto const reward =
ctx_.tx[~sfSignatureReward];
1563 auto const minAccountCreate =
ctx_.tx[~sfMinAccountCreateAmount];
1564 bool const clearAccountCreate =
ctx_.tx.isFlag(tfClearAccountCreateAmount);
1578 (*sleBridge)[sfSignatureReward] = *reward;
1579 if (minAccountCreate)
1581 (*sleBridge)[sfMinAccountCreateAmount] = *minAccountCreate;
1583 if (clearAccountCreate && sleBridge->isFieldPresent(sfMinAccountCreateAmount))
1585 sleBridge->makeFieldAbsent(sfMinAccountCreateAmount);
1587 ctx_.view().update(sleBridge);
1598 auto const amount = ctx.
tx[sfAmount];
1600 if (amount.signum() <= 0 ||
1615 STAmount const& thisChainAmount = ctx.
tx[sfAmount];
1616 auto const claimID = ctx.
tx[sfXChainClaimID];
1618 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
1629 auto const thisDoor = (*sleBridge)[sfAccount];
1630 bool isLockingChain =
false;
1634 isLockingChain =
true;
1638 isLockingChain =
false;
1669 auto const otherChainAmount = [&]() ->
STAmount {
1691 if ((*sleClaimID)[sfAccount] != account)
1708 auto const dst =
ctx_.tx[sfDestination];
1711 auto const claimID =
ctx_.tx[sfXChainClaimID];
1723 auto const scopeResult = [&]() -> std::expected<ScopeResult, TER> {
1730 auto const sleBridge = peekBridge(psb, bridgeSpec);
1731 auto const sleClaimID = psb.
peek(claimIDKeylet);
1733 if (!(sleBridge && sleClaimID && sleAcct))
1736 AccountID const thisDoor = (*sleBridge)[sfAccount];
1755 auto const sendingAmount = [&]() ->
STAmount {
1761 auto const [signersList, quorum, slTer] =
1762 getSignersListAndQuorum(
ctx_.view(), *sleBridge,
ctx_.journal);
1769 auto const claimR = onClaim(
1777 if (!claimR.has_value())
1781 .rewardAccounts = claimR.value(),
1782 .rewardPoolSrc = (*sleClaimID)[sfAccount],
1783 .sendingAmount = sendingAmount,
1784 .srcChain = srcChain,
1785 .signatureReward = (*sleClaimID)[sfSignatureReward],
1789 if (!scopeResult.has_value())
1790 return scopeResult.error();
1792 auto const& [rewardAccounts, rewardPoolSrc, sendingAmount, srcChain, signatureReward] =
1793 scopeResult.
value();
1796 auto const r = finalizeClaimHelper(
1808 OnTransferFail::KeepClaim,
1809 DepositAuthPolicy::DstCanBypass,
1811 if (!r.isTesSuccess())
1824 auto const maxSpend = [&] {
1825 auto const amount = ctx.
tx[sfAmount];
1826 if (amount.native() && amount.signum() > 0)
1827 return amount.xrp();
1837 auto const amount = ctx.
tx[sfAmount];
1838 auto const bridgeSpec = ctx.
tx[sfXChainBridge];
1840 if (amount.signum() <= 0 || !
isLegalNet(amount))
1843 if (amount.asset() != bridgeSpec.lockingChainIssue() &&
1844 amount.asset() != bridgeSpec.issuingChainIssue())
1853 auto const bridgeSpec = ctx.
tx[sfXChainBridge];
1854 auto const amount = ctx.
tx[sfAmount];
1856 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
1862 AccountID const thisDoor = (*sleBridge)[sfAccount];
1865 if (thisDoor == account)
1871 bool isLockingChain =
false;
1873 if (thisDoor == bridgeSpec.lockingChainDoor())
1875 isLockingChain =
true;
1877 else if (thisDoor == bridgeSpec.issuingChainDoor())
1879 isLockingChain =
false;
1889 if (bridgeSpec.lockingChainIssue() != ctx.
tx[sfAmount].asset())
1894 if (bridgeSpec.issuingChainIssue() != ctx.
tx[sfAmount].asset())
1906 auto const account =
ctx_.tx[sfAccount];
1907 auto const amount =
ctx_.tx[sfAmount];
1908 auto const bridgeSpec =
ctx_.tx[sfXChainBridge];
1914 auto const sleBridge = readBridge(psb, bridgeSpec);
1918 auto const dst = (*sleBridge)[sfAccount];
1921 TransferHelperSubmittingAccountInfo submittingAccountInfo{
1924 .postFeeBalance = (*sleAccount)[sfBalance]};
1926 auto const thTer = transferHelper(
1933 CanCreateDstPolicy::No,
1934 DepositAuthPolicy::Normal,
1935 submittingAccountInfo,
1951 auto const reward = ctx.
tx[sfSignatureReward];
1962 auto const account = ctx.
tx[sfAccount];
1963 auto const bridgeSpec = ctx.
tx[sfXChainBridge];
1964 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
1972 auto const reward = ctx.
tx[sfSignatureReward];
1974 if (reward != (*sleBridge)[sfSignatureReward])
1985 auto const balance = (*sleAcc)[sfBalance];
1988 if (balance < reserve)
1998 auto const account =
ctx_.tx[sfAccount];
1999 auto const bridgeSpec =
ctx_.tx[sfXChainBridge];
2000 auto const reward =
ctx_.tx[sfSignatureReward];
2001 auto const otherChainSrc =
ctx_.tx[sfOtherChainSource];
2007 auto const sleBridge = peekBridge(
ctx_.view(), bridgeSpec);
2011 std::uint32_t const claimID = (*sleBridge)[sfXChainClaimID] + 1;
2018 (*sleBridge)[sfXChainClaimID] = claimID;
2021 if (
ctx_.view().exists(claimIDKeylet))
2029 (*sleClaimID)[sfAccount] = account;
2030 (*sleClaimID)[sfXChainBridge] = bridgeSpec;
2031 (*sleClaimID)[sfXChainClaimID] = claimID;
2032 (*sleClaimID)[sfOtherChainSource] = otherChainSrc;
2033 (*sleClaimID)[sfSignatureReward] = reward;
2034 sleClaimID->setFieldArray(sfXChainClaimAttestations,
STArray{sfXChainClaimAttestations});
2038 auto const page =
ctx_.view().dirInsert(
2042 (*sleClaimID)[sfOwnerNode] = *page;
2047 ctx_.view().insert(sleClaimID);
2048 ctx_.view().update(sleBridge);
2049 ctx_.view().update(sleAcct);
2059 return attestationPreflight<Attestations::AttestationClaim>(ctx);
2065 return attestationPreclaim<Attestations::AttestationClaim>(ctx);
2071 return attestationDoApply<Attestations::AttestationClaim>(
ctx_);
2079 return attestationPreflight<Attestations::AttestationCreateAccount>(ctx);
2085 return attestationPreclaim<Attestations::AttestationCreateAccount>(ctx);
2091 return attestationDoApply<Attestations::AttestationCreateAccount>(
ctx_);
2099 auto const amount = ctx.
tx[sfAmount];
2101 if (amount.signum() <= 0 || !amount.native())
2104 auto const reward = ctx.
tx[sfSignatureReward];
2105 if (reward.signum() < 0 || !reward.native())
2108 if (reward.asset() != amount.asset())
2119 STAmount const reward = ctx.
tx[sfSignatureReward];
2121 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
2127 if (reward != (*sleBridge)[sfSignatureReward])
2134 if (!minCreateAmount)
2137 if (amount < *minCreateAmount)
2140 if (minCreateAmount->asset() != amount.
asset())
2143 AccountID const thisDoor = (*sleBridge)[sfAccount];
2145 if (thisDoor == account)
2168 if (bridgeSpec.
issue(srcChain) != ctx.
tx[sfAmount].asset())
2191 auto const sleBridge = peekBridge(psb, bridge);
2195 auto const dst = (*sleBridge)[sfAccount];
2198 TransferHelperSubmittingAccountInfo submittingAccountInfo{
2201 .postFeeBalance = (*sle)[sfBalance]};
2202 STAmount const toTransfer = amount + reward;
2203 auto const thTer = transferHelper(
2210 CanCreateDstPolicy::Yes,
2211 DepositAuthPolicy::Normal,
2212 submittingAccountInfo,
2218 (*sleBridge)[sfXChainAccountCreateCount] = (*sleBridge)[sfXChainAccountCreateCount] + 1;
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
State information when applying a tx.
Writeable view to a ledger, for applying a transaction.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static RoundingMode setround(RoundingMode inMode)
static RoundingMode getround()
A wrapper which makes credits unavailable to balances.
void apply(RawView &to)
Apply changes to base view.
Interface for ledger entry changes.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
void setIssue(Asset const &asset)
Set the Issue for this amount.
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Asset const & asset() const
STAmount const & value() const noexcept
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
std::shared_ptr< STLedgerEntry const > const_pointer
bool isFlag(std::uint32_t) const
void setAccountID(SField const &field, AccountID const &)
AccountID const & issuingChainDoor() const
static ChainType dstChain(bool wasLockingChainSend)
AccountID const & lockingChainDoor() const
static ChainType srcChain(bool wasLockingChainSend)
Issue const & issue(ChainType ct) const
Issue const & issuingChainIssue() const
static ChainType otherChain(ChainType ct)
Issue const & lockingChainIssue() const
static std::expected< std::vector< SignerEntry >, NotTEC > deserialize(STObject const &obj, beast::Journal journal, std::string_view annotation)
AccountID const accountID_
Class describing the consequences to the account of applying a transaction if the transaction consume...
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
void update(SLE::ref sle) override
Indicate changes to a peeked SLE.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Keylet signerList(AccountID const &account) noexcept
A SignerList.
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
std::uint32_t ownerCount(Env const &env, Account const &account)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
STAmount divide(STAmount const &amount, Rate const &rate)
constexpr size_t kXbridgeMaxAccountCreateClaims
bool isTerRetry(TER x) noexcept
bool isXRP(AccountID const &c)
bool isLegalNet(STAmount const &value)
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
std::pair< PublicKey, SecretKey > generateKeyPair(KeyType type, Seed const &seed)
Generate a key pair deterministically.
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
TERSubset< CanCvtToNotTEC > NotTEC
bool isTefFailure(TER x) noexcept
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
void adjustOwnerCount(ApplyView &view, SLE::ref sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
AccountID calcAccountID(PublicKey const &pk)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
@ temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
@ temXCHAIN_BRIDGE_BAD_ISSUES
@ temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
@ tecXCHAIN_NO_SIGNERS_LIST
@ tecXCHAIN_SENDING_ACCOUNT_MISMATCH
@ tecXCHAIN_BAD_TRANSFER_ISSUE
@ tecXCHAIN_PROOF_UNKNOWN_KEY
@ tecXCHAIN_ACCOUNT_CREATE_PAST
@ tecXCHAIN_PAYMENT_FAILED
@ tecXCHAIN_ACCOUNT_CREATE_TOO_MANY
@ tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE
@ tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
@ tecXCHAIN_CREATE_ACCOUNT_DISABLED
@ tecINSUFFICIENT_RESERVE
@ tecXCHAIN_CLAIM_NO_QUORUM
@ tecXCHAIN_REWARD_MISMATCH
bool isTecClaim(TER x) noexcept
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.