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>
110 checkAttestationPublicKey(
111 ReadView
const& view,
113 AccountID const& attestationSignerAccount,
117 if (!signersList.
contains(attestationSignerAccount))
124 if (
auto const sleAttestationSigningAccount =
127 if (accountFromPK == attestationSignerAccount)
130 if (sleAttestationSigningAccount->getFieldU32(
sfFlags) &
133 JLOG(j.
trace()) <<
"Attempt to add an attestation with "
134 "disabled master key.";
143 regularKey != accountFromPK)
148 <<
"Attempt to add an attestation with "
149 "account present and non-present regular key.";
153 JLOG(j.
trace()) <<
"Attempt to add an attestation with "
154 "account present and mismatched "
155 "regular key/public key.";
167 <<
"Attempt to add an attestation with non-existant account "
168 "and mismatched pk/account pair.";
187 enum class CheckDst {
check, ignore };
188 template <
class TAttestation>
189 Expected<std::vector<AccountID>,
TER>
191 XChainAttestationsBase<TAttestation>& attestations,
192 ReadView
const& view,
193 typename TAttestation::MatchFields
const& toMatch,
202 attestations.erase_if([&](
auto const& a) {
203 return checkAttestationPublicKey(
204 view, signersList, a.keyAccount, a.publicKey, j) !=
210 rewardAccounts.reserve(attestations.size());
212 for (
auto const& a : attestations)
214 auto const matchR = a.match(toMatch);
219 if (matchR == nonDstMismatch ||
220 (checkDst == CheckDst::check && matchR != match))
222 auto i = signersList.
find(a.keyAccount);
223 if (i == signersList.
end())
229 rewardAccounts.push_back(a.rewardAccount);
232 if (weight >= quorum)
233 return rewardAccounts;
269 struct OnNewAttestationResult
277 template <
class TAttestation>
278 [[nodiscard]] OnNewAttestationResult
280 XChainAttestationsBase<TAttestation>& attestations,
281 ReadView
const& view,
282 typename TAttestation::TSignedAttestation
const* attBegin,
283 typename TAttestation::TSignedAttestation
const* attEnd,
288 bool changed =
false;
289 for (
auto att = attBegin; att != attEnd; ++att)
291 if (checkAttestationPublicKey(
294 att->attestationSignerAccount,
305 auto const& claimSigningAccount = att->attestationSignerAccount;
307 attestations.begin(),
310 return a.keyAccount == claimSigningAccount;
312 i != attestations.end())
316 *i = TAttestation{*att};
321 attestations.emplace_back(*att);
326 auto r = claimHelper(
329 typename TAttestation::MatchFields{*attBegin},
336 return {std::nullopt, changed};
338 return {std::move(r.value()), changed};
344 Expected<std::vector<AccountID>,
TER>
346 XChainClaimAttestations& attestations,
347 ReadView
const& view,
348 STAmount
const& sendingAmount,
349 bool wasLockingChainSend,
354 XChainClaimAttestation::MatchFields toMatch{
355 sendingAmount, wasLockingChainSend, std::nullopt};
357 attestations, view, toMatch, CheckDst::ignore, quorum, signersList, j);
360 enum class CanCreateDstPolicy {
no,
yes };
362 enum class DepositAuthPolicy { normal, dstCanBypass };
366 struct TransferHelperSubmittingAccountInfo
369 STAmount preFeeBalance;
370 STAmount postFeeBalance;
403 CanCreateDstPolicy canCreate,
404 DepositAuthPolicy depositAuthPolicy,
406 submittingAccountInfo,
413 if (
auto sleDst = psb.read(dstK))
423 bool const canBypassDepositAuth = dst == claimOwner &&
424 depositAuthPolicy == DepositAuthPolicy::dstCanBypass;
426 if (!canBypassDepositAuth && (sleDst->getFlags() &
lsfDepositAuth) &&
432 else if (!amt.native() || canCreate == CanCreateDstPolicy::no)
446 auto const reserve = psb.fees().accountReserve(ownerCount);
448 auto const availableBalance = [&]() -> STAmount {
449 STAmount
const curBal = (*sleSrc)[
sfBalance];
453 if (!submittingAccountInfo ||
454 submittingAccountInfo->account != src ||
455 submittingAccountInfo->postFeeBalance != curBal)
457 return submittingAccountInfo->preFeeBalance;
460 if (availableBalance < amt + reserve)
466 auto sleDst = psb.peek(dstK);
469 if (canCreate == CanCreateDstPolicy::no)
474 if (amt < psb.fees().accountReserve(0))
476 JLOG(j.
trace()) <<
"Insufficient payment to create account.";
484 sleDst = std::make_shared<SLE>(dstK);
499 auto const result =
flow(
513 if (
auto const r = result.result();
524 enum class OnTransferFail {
531 struct FinalizeClaimHelperResult
552 if ((!mainFundsTer || *mainFundsTer ==
tesSUCCESS) &&
561 return *mainFundsTer;
570 if (mainFundsTer && mainFundsTer !=
tesSUCCESS)
571 return *mainFundsTer;
608 FinalizeClaimHelperResult
610 PaymentSandbox& outerSb,
611 STXChainBridge
const& bridgeSpec,
615 STAmount
const& sendingAmount,
617 STAmount
const& rewardPool,
620 Keylet
const& claimIDKeylet,
621 OnTransferFail onTransferFail,
622 DepositAuthPolicy depositAuthPolicy,
625 FinalizeClaimHelperResult result;
629 STAmount
const thisChainAmount = [&] {
630 STAmount r = sendingAmount;
631 r.setIssue(bridgeSpec.issue(dstChain));
634 auto const& thisDoor = bridgeSpec.door(dstChain);
637 PaymentSandbox innerSb{&outerSb};
648 result.mainFundsTer = transferHelper(
655 CanCreateDstPolicy::yes,
661 onTransferFail == OnTransferFail::keepClaim)
667 result.rewardTer = [&]() ->
TER {
668 if (rewardAccounts.empty())
674 STAmount
const share = [&] {
675 STAmount
const den{rewardAccounts.size()};
676 return divide(rewardPool, den, rewardPool.issue());
678 STAmount distributed = rewardPool.zeroed();
679 for (
auto const& rewardAccount : rewardAccounts)
681 auto const thTer = transferHelper(
689 CanCreateDstPolicy::no,
690 DepositAuthPolicy::normal,
698 distributed += share;
704 if (distributed > rewardPool)
711 (onTransferFail == OnTransferFail::keepClaim ||
723 innerSb.apply(outerSb);
727 if (
auto const sleClaimID = outerSb.peek(claimIDKeylet))
729 auto const cidOwner = (*sleClaimID)[
sfAccount];
734 if (!outerSb.dirRemove(
738 <<
"Unable to delete xchain seq number from owner.";
744 outerSb.erase(sleClaimID);
763 getSignersListAndQuorum(
764 ReadView
const& view,
765 SLE const& sleBridge,
772 auto const sleDoor = [&] {
return view.read(
keylet::account(thisDoor)); }();
793 for (
auto const& as : *accountSigners)
795 r[as.account] = as.weight;
801 template <
class R,
class F>
803 readOrpeekBridge(F&& getter, STXChainBridge
const& bridgeSpec)
806 if (
auto r = getter(bridgeSpec, ct))
819 peekBridge(ApplyView& v, STXChainBridge
const& bridgeSpec)
821 return readOrpeekBridge<SLE>(
828 readBridge(ReadView
const& v, STXChainBridge
const& bridgeSpec)
830 return readOrpeekBridge<SLE const>(
840 template <
class TIter>
842 applyClaimAttestations(
847 STXChainBridge
const& bridgeSpec,
853 if (attBegin == attEnd)
856 PaymentSandbox psb(&view);
858 auto const claimIDKeylet =
863 OnNewAttestationResult newAttResult;
864 STAmount rewardAmount;
868 auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
873 auto const sleClaimID = psb.peek(claimIDKeylet);
880 for (
auto att = attBegin; att != attEnd; ++att)
882 if (!signersList.
contains(att->attestationSignerAccount))
893 if (attBegin->sendingAccount != otherChainSource)
905 if (attDstChain != dstChain)
911 XChainClaimAttestations curAtts{
914 auto const newAttResult = onNewAttestations(
918 &atts[0] + atts.
size(),
924 sleClaimID->setFieldArray(
926 psb.update(sleClaimID);
934 if (!scopeResult.has_value())
935 return scopeResult.error();
937 auto const& [newAttResult, rewardAmount, cidOwner] = scopeResult.value();
938 auto const& [rewardAccounts, attListChanged] = newAttResult;
939 if (rewardAccounts && attBegin->dst)
941 auto const r = finalizeClaimHelper(
947 attBegin->sendingAmount,
953 OnTransferFail::keepClaim,
954 DepositAuthPolicy::normal,
957 auto const rTer = r.ter();
969 template <
class TIter>
971 applyCreateAccountAttestations(
978 STXChainBridge
const& bridgeSpec,
979 Keylet
const& bridgeK,
985 if (attBegin == attEnd)
988 PaymentSandbox psb(&view);
990 auto const claimCountResult = [&]() -> Expected<std::uint64_t, TER> {
991 auto const sleBridge = psb.peek(bridgeK);
998 if (!claimCountResult.has_value())
999 return claimCountResult.error();
1003 if (attBegin->createCount <= claimCount)
1020 if (attDstChain != dstChain)
1026 auto const claimIDKeylet =
1031 OnNewAttestationResult newAttResult;
1033 XChainCreateAccountAttestations curAtts;
1036 auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
1044 auto const sleClaimID = psb.peek(claimIDKeylet);
1045 bool createCID =
false;
1050 auto const sleDoor = psb.peek(doorK);
1055 auto const balance = (*sleDoor)[
sfBalance];
1056 auto const reserve =
1057 psb.fees().accountReserve((*sleDoor)[
sfOwnerCount] + 1);
1059 if (balance < reserve)
1065 for (
auto att = attBegin; att != attEnd; ++att)
1067 if (!signersList.
contains(att->attestationSignerAccount))
1076 XChainCreateAccountAttestations curAtts = [&] {
1078 return XChainCreateAccountAttestations{
1079 sleClaimID->getFieldArray(
1081 return XChainCreateAccountAttestations{};
1084 auto const newAttResult = onNewAttestations(
1088 &atts[0] + atts.
size(),
1099 sleClaimID->setFieldArray(
1101 psb.update(sleClaimID);
1103 return ScopeResult{newAttResult, createCID, curAtts};
1106 if (!scopeResult.has_value())
1107 return scopeResult.error();
1109 auto const& [attResult, createCID, curAtts] = scopeResult.value();
1110 auto const& [rewardAccounts, attListChanged] = attResult;
1113 if (rewardAccounts && claimCount + 1 == attBegin->createCount)
1115 auto const r = finalizeClaimHelper(
1121 attBegin->sendingAmount,
1123 attBegin->rewardAmount,
1127 OnTransferFail::removeClaim,
1128 DepositAuthPolicy::normal,
1131 auto const rTer = r.ter();
1141 auto const sleBridge = psb.peek(bridgeK);
1145 psb.update(sleBridge);
1149 auto const createdSleClaimID = std::make_shared<SLE>(claimIDKeylet);
1150 (*createdSleClaimID)[
sfAccount] = doorAccount;
1153 attBegin->createCount;
1154 createdSleClaimID->setFieldArray(
1158 auto const page = psb.dirInsert(
1166 auto const sleDoor = psb.peek(doorK);
1172 psb.insert(createdSleClaimID);
1173 psb.update(sleDoor);
1181 template <
class TAttestation>
1183 toClaim(STTx
const& tx)
1186 std::is_same_v<TAttestation, Attestations::AttestationClaim> ||
1187 std::is_same_v<TAttestation, Attestations::AttestationCreateAccount>);
1193 return TAttestation(o);
1198 return std::nullopt;
1201 template <
class TAttestation>
1203 attestationPreflight(PreflightContext
const& ctx)
1217 auto const att = toClaim<TAttestation>(ctx.tx);
1222 if (!att->verify(bridgeSpec))
1224 if (!att->validAmounts())
1227 if (att->sendingAmount.signum() <= 0)
1229 auto const expectedIssue =
1231 if (att->sendingAmount.issue() != expectedIssue)
1237 template <
class TAttestation>
1239 attestationPreclaim(PreclaimContext
const& ctx)
1241 auto const att = toClaim<TAttestation>(ctx.tx);
1246 auto const sleBridge = readBridge(ctx.view, bridgeSpec);
1252 AccountID const attestationSignerAccount{
1257 auto const [signersList, quorum, slTer] =
1258 getSignersListAndQuorum(ctx.view, *sleBridge, ctx.j);
1263 return checkAttestationPublicKey(
1264 ctx.view, signersList, attestationSignerAccount, pk, ctx.j);
1267 template <
class TAttestation>
1269 attestationDoApply(ApplyContext& ctx)
1271 auto const att = toClaim<TAttestation>(ctx.tx);
1287 auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
1292 auto sleBridge = readBridge(ctx.view(), bridgeSpec);
1297 Keylet
const bridgeK{
ltBRIDGE, sleBridge->key()};
1302 if (thisDoor == bridgeSpec.lockingChainDoor())
1304 else if (thisDoor == bridgeSpec.issuingChainDoor())
1313 auto [signersList, quorum, slTer] =
1314 getSignersListAndQuorum(ctx.view(), *sleBridge, ctx.journal);
1320 srcChain, std::move(signersList), quorum, thisDoor, bridgeK};
1323 if (!scopeResult.has_value())
1324 return scopeResult.error();
1326 auto const& [srcChain, signersList, quorum, thisDoor, bridgeK] =
1327 scopeResult.value();
1330 std::is_same_v<TAttestation, Attestations::AttestationClaim> ||
1331 std::is_same_v<TAttestation, Attestations::AttestationCreateAccount>);
1333 if constexpr (std::is_same_v<TAttestation, Attestations::AttestationClaim>)
1335 return applyClaimAttestations(
1348 Attestations::AttestationCreateAccount>)
1350 return applyCreateAccountAttestations(
1386 if (bridgeSpec.lockingChainDoor() == bridgeSpec.issuingChainDoor())
1391 if (bridgeSpec.lockingChainDoor() != account &&
1392 bridgeSpec.issuingChainDoor() != account)
1397 if (
isXRP(bridgeSpec.lockingChainIssue()) !=
1398 isXRP(bridgeSpec.issuingChainIssue()))
1405 if (!
isXRP(reward) || reward.signum() < 0)
1410 if (minAccountCreate &&
1411 ((!
isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1412 !
isXRP(bridgeSpec.lockingChainIssue()) ||
1413 !
isXRP(bridgeSpec.issuingChainIssue())))
1418 if (
isXRP(bridgeSpec.issuingChainIssue()))
1427 if (bridgeSpec.issuingChainDoor() != rootAccount)
1436 if (bridgeSpec.issuingChainDoor() !=
1437 bridgeSpec.issuingChainIssue().account)
1443 if (bridgeSpec.lockingChainDoor() == bridgeSpec.lockingChainIssue().account)
1473 if (!
isXRP(bridgeSpec.issue(chainType)))
1475 auto const sleIssuer =
1493 auto const balance = (*sleAcc)[
sfBalance];
1494 auto const reserve =
1497 if (balance < reserve)
1520 auto const sleBridge = std::make_shared<SLE>(bridgeKeylet);
1524 if (minAccountCreate)
1566 bool const clearAccountCreate =
1569 if (!reward && !minAccountCreate && !clearAccountCreate)
1575 if (minAccountCreate && clearAccountCreate)
1581 if (bridgeSpec.lockingChainDoor() != account &&
1582 bridgeSpec.issuingChainDoor() != account)
1587 if (reward && (!
isXRP(*reward) || reward->signum() < 0))
1592 if (minAccountCreate &&
1593 ((!
isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1594 !
isXRP(bridgeSpec.lockingChainIssue()) ||
1595 !
isXRP(bridgeSpec.issuingChainIssue())))
1627 bool const clearAccountCreate =
1637 auto const sleBridge =
1644 if (minAccountCreate)
1648 if (clearAccountCreate &&
1675 if (amount.signum() <= 0 ||
1693 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
1704 auto const thisDoor = (*sleBridge)[
sfAccount];
1705 bool isLockingChain =
false;
1708 isLockingChain =
true;
1710 isLockingChain =
false;
1739 auto const otherChainAmount = [&]() ->
STAmount {
1748 auto const sleClaimID =
1758 if ((*sleClaimID)[
sfAccount] != account)
1797 auto const sleBridge = peekBridge(psb, bridgeSpec);
1798 auto const sleClaimID = psb.
peek(claimIDKeylet);
1800 if (!(sleBridge && sleClaimID && sleAcct))
1817 auto const sendingAmount = [&]() ->
STAmount {
1823 auto const [signersList, quorum, slTer] =
1832 auto const claimR = onClaim(
1841 if (!claimR.has_value())
1853 if (!scopeResult.has_value())
1854 return scopeResult.error();
1856 auto const& [rewardAccounts, rewardPoolSrc, sendingAmount, srcChain, signatureReward] =
1857 scopeResult.value();
1860 auto const r = finalizeClaimHelper(
1872 OnTransferFail::keepClaim,
1873 DepositAuthPolicy::dstCanBypass,
1875 if (!r.isTesSuccess())
1888 auto const maxSpend = [&] {
1890 if (amount.native() && amount.signum() > 0)
1891 return amount.xrp();
1913 if (amount.signum() <= 0 || !
isLegalNet(amount))
1916 if (amount.issue() != bridgeSpec.lockingChainIssue() &&
1917 amount.issue() != bridgeSpec.issuingChainIssue())
1929 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
1938 if (thisDoor == account)
1944 bool isLockingChain =
false;
1946 if (thisDoor == bridgeSpec.lockingChainDoor())
1947 isLockingChain =
true;
1948 else if (thisDoor == bridgeSpec.issuingChainDoor())
1949 isLockingChain =
false;
1956 if (bridgeSpec.lockingChainIssue() != ctx.
tx[
sfAmount].issue())
1961 if (bridgeSpec.issuingChainIssue() != ctx.
tx[
sfAmount].issue())
1980 auto const sleBridge = readBridge(psb, bridgeSpec);
1984 auto const dst = (*sleBridge)[
sfAccount];
1987 TransferHelperSubmittingAccountInfo submittingAccountInfo{
1990 auto const thTer = transferHelper(
1997 CanCreateDstPolicy::no,
1998 DepositAuthPolicy::normal,
1999 submittingAccountInfo,
2037 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
2058 auto const balance = (*sleAcc)[
sfBalance];
2059 auto const reserve =
2062 if (balance < reserve)
2081 auto const sleBridge = peekBridge(
ctx_.
view(), bridgeSpec);
2095 auto const sleClaimID = std::make_shared<SLE>(claimIDKeylet);
2102 sleClaimID->setFieldArray(
2130 return attestationPreflight<Attestations::AttestationClaim>(ctx);
2136 return attestationPreclaim<Attestations::AttestationClaim>(ctx);
2142 return attestationDoApply<Attestations::AttestationClaim>(
ctx_);
2150 return attestationPreflight<Attestations::AttestationCreateAccount>(ctx);
2156 return attestationPreclaim<Attestations::AttestationCreateAccount>(ctx);
2162 return attestationDoApply<Attestations::AttestationCreateAccount>(
ctx_);
2181 if (amount.signum() <= 0 || !amount.native())
2185 if (reward.signum() < 0 || !reward.native())
2188 if (reward.issue() != amount.issue())
2201 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
2215 if (!minCreateAmount)
2218 if (amount < *minCreateAmount)
2221 if (minCreateAmount->issue() != amount.
issue())
2226 if (thisDoor == account)
2267 auto const sleBridge = peekBridge(psb, bridge);
2271 auto const dst = (*sleBridge)[
sfAccount];
2274 TransferHelperSubmittingAccountInfo submittingAccountInfo{
2276 STAmount const toTransfer = amount + reward;
2277 auto const thTer = transferHelper(
2284 CanCreateDstPolicy::yes,
2285 DepositAuthPolicy::normal,
2286 submittingAccountInfo,