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)
445 auto const ownerCount = sleSrc->getFieldU32(
sfOwnerCount);
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)
1214 auto const att = toClaim<TAttestation>(ctx.tx);
1219 if (!att->verify(bridgeSpec))
1221 if (!att->validAmounts())
1224 if (att->sendingAmount.signum() <= 0)
1226 auto const expectedIssue =
1228 if (att->sendingAmount.issue() != expectedIssue)
1234 template <
class TAttestation>
1236 attestationPreclaim(PreclaimContext
const& ctx)
1238 auto const att = toClaim<TAttestation>(ctx.tx);
1243 auto const sleBridge = readBridge(ctx.view, bridgeSpec);
1249 AccountID const attestationSignerAccount{
1254 auto const [signersList, quorum, slTer] =
1255 getSignersListAndQuorum(ctx.view, *sleBridge, ctx.j);
1260 return checkAttestationPublicKey(
1261 ctx.view, signersList, attestationSignerAccount, pk, ctx.j);
1264 template <
class TAttestation>
1266 attestationDoApply(ApplyContext& ctx)
1268 auto const att = toClaim<TAttestation>(ctx.tx);
1284 auto const scopeResult = [&]() -> Expected<ScopeResult, TER> {
1289 auto sleBridge = readBridge(ctx.view(), bridgeSpec);
1294 Keylet
const bridgeK{
ltBRIDGE, sleBridge->key()};
1299 if (thisDoor == bridgeSpec.lockingChainDoor())
1301 else if (thisDoor == bridgeSpec.issuingChainDoor())
1310 auto [signersList, quorum, slTer] =
1311 getSignersListAndQuorum(ctx.view(), *sleBridge, ctx.journal);
1317 srcChain, std::move(signersList), quorum, thisDoor, bridgeK};
1320 if (!scopeResult.has_value())
1321 return scopeResult.error();
1323 auto const& [srcChain, signersList, quorum, thisDoor, bridgeK] =
1324 scopeResult.value();
1327 std::is_same_v<TAttestation, Attestations::AttestationClaim> ||
1328 std::is_same_v<TAttestation, Attestations::AttestationCreateAccount>);
1330 if constexpr (std::is_same_v<TAttestation, Attestations::AttestationClaim>)
1332 return applyClaimAttestations(
1345 Attestations::AttestationCreateAccount>)
1347 return applyCreateAccountAttestations(
1383 if (bridgeSpec.lockingChainDoor() == bridgeSpec.issuingChainDoor())
1388 if (bridgeSpec.lockingChainDoor() != account &&
1389 bridgeSpec.issuingChainDoor() != account)
1394 if (
isXRP(bridgeSpec.lockingChainIssue()) !=
1395 isXRP(bridgeSpec.issuingChainIssue()))
1402 if (!
isXRP(reward) || reward.signum() < 0)
1407 if (minAccountCreate &&
1408 ((!
isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1409 !
isXRP(bridgeSpec.lockingChainIssue()) ||
1410 !
isXRP(bridgeSpec.issuingChainIssue())))
1415 if (
isXRP(bridgeSpec.issuingChainIssue()))
1424 if (bridgeSpec.issuingChainDoor() != rootAccount)
1433 if (bridgeSpec.issuingChainDoor() !=
1434 bridgeSpec.issuingChainIssue().account)
1440 if (bridgeSpec.lockingChainDoor() == bridgeSpec.lockingChainIssue().account)
1464 if (!
isXRP(bridgeSpec.issue(chainType)))
1466 auto const sleIssuer =
1484 auto const balance = (*sleAcc)[
sfBalance];
1485 auto const reserve =
1488 if (balance < reserve)
1511 auto const sleBridge = std::make_shared<SLE>(bridgeKeylet);
1515 if (minAccountCreate)
1557 bool const clearAccountCreate =
1560 if (!reward && !minAccountCreate && !clearAccountCreate)
1566 if (minAccountCreate && clearAccountCreate)
1572 if (bridgeSpec.lockingChainDoor() != account &&
1573 bridgeSpec.issuingChainDoor() != account)
1578 if (reward && (!
isXRP(*reward) || reward->signum() < 0))
1583 if (minAccountCreate &&
1584 ((!
isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
1585 !
isXRP(bridgeSpec.lockingChainIssue()) ||
1586 !
isXRP(bridgeSpec.issuingChainIssue())))
1618 bool const clearAccountCreate =
1628 auto const sleBridge =
1635 if (minAccountCreate)
1639 if (clearAccountCreate &&
1666 if (amount.signum() <= 0 ||
1684 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
1695 auto const thisDoor = (*sleBridge)[
sfAccount];
1696 bool isLockingChain =
false;
1699 isLockingChain =
true;
1701 isLockingChain =
false;
1730 auto const otherChainAmount = [&]() ->
STAmount {
1739 auto const sleClaimID =
1749 if ((*sleClaimID)[
sfAccount] != account)
1788 auto const sleBridge = peekBridge(psb, bridgeSpec);
1789 auto const sleClaimID = psb.
peek(claimIDKeylet);
1791 if (!(sleBridge && sleClaimID && sleAcct))
1808 auto const sendingAmount = [&]() ->
STAmount {
1814 auto const [signersList, quorum, slTer] =
1823 auto const claimR = onClaim(
1832 if (!claimR.has_value())
1844 if (!scopeResult.has_value())
1845 return scopeResult.error();
1847 auto const& [rewardAccounts, rewardPoolSrc, sendingAmount, srcChain, signatureReward] =
1848 scopeResult.value();
1851 auto const r = finalizeClaimHelper(
1863 OnTransferFail::keepClaim,
1864 DepositAuthPolicy::dstCanBypass,
1866 if (!r.isTesSuccess())
1879 auto const maxSpend = [&] {
1881 if (amount.native() && amount.signum() > 0)
1882 return amount.xrp();
1904 if (amount.signum() <= 0 || !
isLegalNet(amount))
1907 if (amount.issue() != bridgeSpec.lockingChainIssue() &&
1908 amount.issue() != bridgeSpec.issuingChainIssue())
1920 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
1929 if (thisDoor == account)
1935 bool isLockingChain =
false;
1937 if (thisDoor == bridgeSpec.lockingChainDoor())
1938 isLockingChain =
true;
1939 else if (thisDoor == bridgeSpec.issuingChainDoor())
1940 isLockingChain =
false;
1947 if (bridgeSpec.lockingChainIssue() != ctx.
tx[
sfAmount].issue())
1952 if (bridgeSpec.issuingChainIssue() != ctx.
tx[
sfAmount].issue())
1971 auto const sleBridge = readBridge(psb, bridgeSpec);
1975 auto const dst = (*sleBridge)[
sfAccount];
1978 TransferHelperSubmittingAccountInfo submittingAccountInfo{
1981 auto const thTer = transferHelper(
1988 CanCreateDstPolicy::no,
1989 DepositAuthPolicy::normal,
1990 submittingAccountInfo,
2028 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
2049 auto const balance = (*sleAcc)[
sfBalance];
2050 auto const reserve =
2053 if (balance < reserve)
2072 auto const sleBridge = peekBridge(
ctx_.
view(), bridgeSpec);
2086 auto const sleClaimID = std::make_shared<SLE>(claimIDKeylet);
2093 sleClaimID->setFieldArray(
2121 return attestationPreflight<Attestations::AttestationClaim>(ctx);
2127 return attestationPreclaim<Attestations::AttestationClaim>(ctx);
2133 return attestationDoApply<Attestations::AttestationClaim>(
ctx_);
2141 return attestationPreflight<Attestations::AttestationCreateAccount>(ctx);
2147 return attestationPreclaim<Attestations::AttestationCreateAccount>(ctx);
2153 return attestationDoApply<Attestations::AttestationCreateAccount>(
ctx_);
2172 if (amount.signum() <= 0 || !amount.native())
2176 if (reward.signum() < 0 || !reward.native())
2179 if (reward.issue() != amount.issue())
2192 auto const sleBridge = readBridge(ctx.
view, bridgeSpec);
2206 if (!minCreateAmount)
2209 if (amount < *minCreateAmount)
2212 if (minCreateAmount->issue() != amount.
issue())
2217 if (thisDoor == account)
2258 auto const sleBridge = peekBridge(psb, bridge);
2262 auto const dst = (*sleBridge)[
sfAccount];
2265 TransferHelperSubmittingAccountInfo submittingAccountInfo{
2267 STAmount const toTransfer = amount + reward;
2268 auto const thTer = transferHelper(
2275 CanCreateDstPolicy::yes,
2276 DepositAuthPolicy::normal,
2277 submittingAccountInfo,