1#include <xrpld/app/misc/AMMHelpers.h>
2#include <xrpld/app/misc/AMMUtils.h>
3#include <xrpld/app/tx/detail/InvariantCheck.h>
4#include <xrpld/app/tx/detail/NFTokenUtils.h>
5#include <xrpld/app/tx/detail/PermissionedDomainSet.h>
7#include <xrpl/basics/Log.h>
8#include <xrpl/beast/utility/instrumentation.h>
9#include <xrpl/ledger/CredentialHelpers.h>
10#include <xrpl/ledger/ReadView.h>
11#include <xrpl/ledger/View.h>
12#include <xrpl/protocol/Feature.h>
13#include <xrpl/protocol/Indexes.h>
14#include <xrpl/protocol/LedgerFormats.h>
15#include <xrpl/protocol/MPTIssue.h>
16#include <xrpl/protocol/SField.h>
17#include <xrpl/protocol/STArray.h>
18#include <xrpl/protocol/STNumber.h>
19#include <xrpl/protocol/SystemParameters.h>
20#include <xrpl/protocol/TER.h>
21#include <xrpl/protocol/TxFormats.h>
22#include <xrpl/protocol/Units.h>
23#include <xrpl/protocol/nftPageMask.h>
72 return safe_cast<Privilege>(
76#pragma push_macro("TRANSACTION")
79#define TRANSACTION(tag, value, name, delegable, amendment, privileges, ...) \
81 return (privileges) & priv; \
89#include <xrpl/protocol/detail/transactions.macro>
98#pragma pop_macro("TRANSACTION")
112 JLOG(j.
fatal()) <<
"Invariant failed: fee paid was negative: " << fee.
drops();
120 JLOG(j.
fatal()) <<
"Invariant failed: fee paid exceeds system limit: " << fee.
drops();
128 JLOG(j.
fatal()) <<
"Invariant failed: fee paid is " << fee.
drops() <<
" exceeds fee specified in transaction.";
152 switch (before->getType())
155 drops_ -= (*before)[sfBalance].xrp().drops();
158 drops_ -= ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops();
161 if (
isXRP((*before)[sfAmount]))
162 drops_ -= (*before)[sfAmount].xrp().drops();
171 switch (
after->getType())
174 drops_ += (*after)[sfBalance].xrp().drops();
178 drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
182 drops_ += (*after)[sfAmount].xrp().drops();
197 JLOG(j.
fatal()) <<
"Invariant failed: XRP net change was positive: " <<
drops_;
204 JLOG(j.
fatal()) <<
"Invariant failed: XRP net change of " <<
drops_ <<
" doesn't match fee " << fee.
drops();
216 auto isBad = [](
STAmount const& balance) {
217 if (!balance.native())
220 auto const drops = balance.xrp();
234 if (before && before->getType() == ltACCOUNT_ROOT)
235 bad_ |= isBad((*before)[sfBalance]);
237 if (
after &&
after->getType() == ltACCOUNT_ROOT)
246 JLOG(j.
fatal()) <<
"Invariant failed: incorrect account XRP balance";
263 if (pays < beast::zero)
266 if (gets < beast::zero)
270 return pays.
native() && gets.native();
273 if (before && before->getType() == ltOFFER)
274 bad_ |= isBad((*before)[sfTakerPays], (*before)[sfTakerGets]);
277 bad_ |= isBad((*
after)[sfTakerPays], (*after)[sfTakerGets]);
285 JLOG(j.
fatal()) <<
"Invariant failed: offer with a bad amount";
300 auto isBad = [](
STAmount const& amount) {
313 if (amount.holds<
Issue>())
315 if (amount <= beast::zero)
325 if (amount <= beast::zero)
335 if (before && before->getType() == ltESCROW)
336 bad_ |= isBad((*before)[sfAmount]);
346 if (
after &&
after->getType() == ltMPTOKEN_ISSUANCE)
348 auto const outstanding = (*after)[sfOutstandingAmount];
349 checkAmount(outstanding);
350 if (
auto const locked = (*
after)[~sfLockedAmount])
352 checkAmount(*locked);
353 bad_ = outstanding < *locked;
359 auto const mptAmount = (*after)[sfMPTAmount];
360 checkAmount(mptAmount);
361 if (
auto const locked = (*
after)[~sfLockedAmount])
363 checkAmount(*locked);
373 JLOG(j.
fatal()) <<
"Invariant failed: escrow specifies invalid amount";
388 if (isDelete && before && before->getType() == ltACCOUNT_ROOT)
410 JLOG(j.
fatal()) <<
"Invariant failed: account deletion "
411 "succeeded without deleting an account";
413 JLOG(j.
fatal()) <<
"Invariant failed: account deletion "
414 "succeeded but deleted multiple accounts!";
427 JLOG(j.
fatal()) <<
"Invariant failed: an account root was deleted";
439 if (isDelete && before && before->getType() == ltACCOUNT_ROOT)
455 [[maybe_unused]]
bool const enforce = view.
rules().
enabled(featureInvariantsV1_1) ||
458 auto const objectExists = [&view, enforce, &j](
auto const& keylet) {
460 if (
auto const sle = view.
read(keylet))
463 auto const typeName = [&sle]() {
467 return item->getName();
471 JLOG(j.
fatal()) <<
"Invariant failed: account deletion left behind a " << typeName <<
" object";
476 "xrpl::AccountRootsDeletedClean::finalize::objectExists : "
477 "account deletion left no objects behind");
485 auto const accountID = before->getAccountID(sfAccount);
487 if (
after->at(sfBalance) != beast::zero)
489 JLOG(j.
fatal()) <<
"Invariant failed: account deletion left "
490 "behind a non-zero balance";
493 "xrpl::AccountRootsDeletedClean::finalize : "
494 "deleted account has zero balance");
499 if (
after->at(sfOwnerCount) != 0)
501 JLOG(j.
fatal()) <<
"Invariant failed: account deletion left "
502 "behind a non-zero owner count";
505 "xrpl::AccountRootsDeletedClean::finalize : "
506 "deleted account has zero owner count");
513 if (objectExists(
std::invoke(keyletfunc, accountID)) && enforce)
528 if (key && objectExists(
Keylet{ltNFTOKEN_PAGE, *key}) && enforce)
536 if (before->isFieldPresent(*field))
538 auto const key = before->getFieldH256(*field);
556 if (before &&
after && before->getType() !=
after->getType())
561#pragma push_macro("LEDGER_ENTRY")
564#define LEDGER_ENTRY(tag, ...) case tag:
566 switch (
after->getType())
568#include <xrpl/protocol/detail/ledger_entries.macro>
577#pragma pop_macro("LEDGER_ENTRY")
589 JLOG(j.
fatal()) <<
"Invariant failed: ledger entry type mismatch";
594 JLOG(j.
fatal()) <<
"Invariant failed: invalid ledger entry type added";
605 if (
after &&
after->getType() == ltRIPPLE_STATE)
621 JLOG(j.
fatal()) <<
"Invariant failed: an XRP trust line was created";
633 if (
after &&
after->getType() == ltRIPPLE_STATE)
657 JLOG(j.
fatal()) <<
"Invariant failed: a trust line with deep freeze flag "
658 "without normal freeze was created";
686 if (balanceChange.signum() == 0)
718 [[maybe_unused]]
bool const enforce = view.
rules().
enabled(featureDeepFreeze);
722 auto const issuerSle =
findIssuer(issue.account, view);
731 "xrpl::TransfersNotFrozen::finalize : enforce "
753 XRPL_ASSERT(
after,
"xrpl::TransfersNotFrozen::isValidEntry : valid after.");
759 if (
after->getType() == ltACCOUNT_ROOT)
771 return after->getType() == ltRIPPLE_STATE && (!before || before->getType() == ltRIPPLE_STATE);
780 auto const getBalance = [](
auto const& line,
auto const& other,
bool zero) {
781 STAmount amt = line ? line->at(sfBalance) : other->at(sfBalance).zeroed();
782 return zero ? amt.
zeroed() : amt;
790 auto const balanceBefore = getBalance(before,
after,
false);
797 auto const balanceAfter = getBalance(
after, before, isDelete);
799 return balanceAfter - balanceBefore;
807 "xrpl::TransfersNotFrozen::recordBalance : valid trustline "
811 changes.senders.emplace_back(std::move(change));
813 changes.receivers.emplace_back(std::move(change));
819 auto const balanceChangeSign = balanceChange.
signum();
820 auto const currency =
after->at(sfBalance).getCurrency();
868 for (
auto const& change : actors)
870 bool const high = change.line->at(sfLowLimit).getIssuer() == issuer->at(sfAccount);
892 bool const frozen = globalFreeze || deepFreeze || freeze;
904 JLOG(j.
debug()) <<
"Invariant check allowing funds to be moved "
905 << (change.
balanceChangeSign > 0 ?
"to" :
"from") <<
" a frozen trustline for AMMClawback "
910 JLOG(j.
fatal()) <<
"Invariant failed: Attempting to move frozen funds for " << tx.
getTransactionID();
914 "xrpl::TransfersNotFrozen::validateFrozenState : enforce "
930 if (!before &&
after->getType() == ltACCOUNT_ROOT)
952 JLOG(j.
fatal()) <<
"Invariant failed: multiple accounts "
953 "created in a single transaction";
960 bool const pseudoAccount =
966 JLOG(j.
fatal()) <<
"Invariant failed: pseudo-account created by a "
967 "wrong transaction type";
975 JLOG(j.
fatal()) <<
"Invariant failed: account created with "
976 "wrong starting sequence number";
985 JLOG(j.
fatal()) <<
"Invariant failed: pseudo-account created with "
994 JLOG(j.
fatal()) <<
"Invariant failed: account root created illegally";
1007 static constexpr uint256 const accountBits = ~pageBits;
1009 if ((before && before->getType() != ltNFTOKEN_PAGE) || (
after &&
after->getType() != ltNFTOKEN_PAGE))
1013 uint256 const account = sle->
key() & accountBits;
1014 uint256 const hiLimit = sle->key() & pageBits;
1022 if (account != (*prev & accountBits))
1025 if (hiLimit <= (*prev & pageBits))
1029 if (
auto const next = (*sle)[~sfNextPageMin])
1031 if (account != (*next & accountBits))
1034 if (hiLimit >= (*next & pageBits))
1039 auto const& nftokens = sle->getFieldArray(sfNFTokens);
1042 if (
std::size_t const nftokenCount = nftokens.size();
1048 uint256 const loLimit = prev ? *prev & pageBits :
uint256(beast::zero);
1052 for (
auto const& obj : nftokens)
1054 uint256 const tokenID = obj[sfNFTokenID];
1061 if (
uint256 const tokenPageBits = tokenID & pageBits;
1062 tokenPageBits < loLimit || tokenPageBits >= hiLimit)
1065 if (
auto uri = obj[~sfURI]; uri && uri->empty())
1087 if (!isDelete && before &&
after)
1095 !
after->isFieldPresent(sfNextPageMin))
1112 JLOG(j.
fatal()) <<
"Invariant failed: NFT page is improperly linked.";
1118 JLOG(j.
fatal()) <<
"Invariant failed: NFT found in incorrect page.";
1124 JLOG(j.
fatal()) <<
"Invariant failed: NFTs on page are not sorted.";
1130 JLOG(j.
fatal()) <<
"Invariant failed: NFT contains empty URI.";
1136 JLOG(j.
fatal()) <<
"Invariant failed: NFT page has invalid size.";
1144 JLOG(j.
fatal()) <<
"Invariant failed: Last NFT page deleted with "
1145 "non-empty directory.";
1150 JLOG(j.
fatal()) <<
"Invariant failed: Lost NextMinPage link.";
1165 if (before && before->getType() == ltACCOUNT_ROOT)
1171 if (
after &&
after->getType() == ltACCOUNT_ROOT)
1190 JLOG(j.
fatal()) <<
"Invariant failed: the number of minted tokens "
1191 "changed without a mint transaction!";
1197 JLOG(j.
fatal()) <<
"Invariant failed: the number of burned tokens "
1198 "changed without a burn transaction!";
1209 JLOG(j.
fatal()) <<
"Invariant failed: successful minting didn't increase "
1210 "the number of minted tokens.";
1216 JLOG(j.
fatal()) <<
"Invariant failed: failed minting changed the "
1217 "number of minted tokens.";
1223 JLOG(j.
fatal()) <<
"Invariant failed: minting changed the number of "
1235 JLOG(j.
fatal()) <<
"Invariant failed: successful burning didn't increase "
1236 "the number of burned tokens.";
1243 JLOG(j.
fatal()) <<
"Invariant failed: failed burning changed the "
1244 "number of burned tokens.";
1250 JLOG(j.
fatal()) <<
"Invariant failed: burning changed the number of "
1264 if (before && before->getType() == ltRIPPLE_STATE)
1267 if (before && before->getType() == ltMPTOKEN)
1286 JLOG(j.
fatal()) <<
"Invariant failed: more than one trustline changed.";
1292 JLOG(j.
fatal()) <<
"Invariant failed: more than one mptokens changed.";
1300 AccountID const& holder = amount.getIssuer();
1303 if (holderBalance.
signum() < 0)
1305 JLOG(j.
fatal()) <<
"Invariant failed: trustline balance is negative";
1314 JLOG(j.
fatal()) <<
"Invariant failed: some trustlines were changed "
1315 "despite failure of the transaction.";
1321 JLOG(j.
fatal()) <<
"Invariant failed: some mptokens were changed "
1322 "despite failure of the transaction.";
1338 if (
after &&
after->getType() == ltMPTOKEN_ISSUANCE)
1354 if (mptIssue.getIssuer() ==
after->at(sfAccount))
1370 auto const& rules = view.
rules();
1372 bool enforceCreatedByIssuer = rules.
enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol);
1375 JLOG(j.
fatal()) <<
"Invariant failed: MPToken created for the MPT issuer";
1378 XRPL_ASSERT_PARTS(enforceCreatedByIssuer,
"xrpl::ValidMPTIssuance::finalize",
"no issuer MPToken");
1379 if (enforceCreatedByIssuer)
1388 JLOG(j.
fatal()) <<
"Invariant failed: transaction "
1389 "succeeded without creating a MPT issuance";
1393 JLOG(j.
fatal()) <<
"Invariant failed: transaction "
1394 "succeeded while removing MPT issuances";
1398 JLOG(j.
fatal()) <<
"Invariant failed: transaction "
1399 "succeeded but created multiple issuances";
1409 JLOG(j.
fatal()) <<
"Invariant failed: MPT issuance deletion "
1410 "succeeded without removing a MPT issuance";
1414 JLOG(j.
fatal()) <<
"Invariant failed: MPT issuance deletion "
1415 "succeeded while creating MPT issuances";
1419 JLOG(j.
fatal()) <<
"Invariant failed: MPT issuance deletion "
1420 "succeeded but deleted multiple issuances";
1426 bool const lendingProtocolEnabled = view.
rules().
enabled(featureLendingProtocol);
1430 bool const enforceEscrowFinish =
1431 (txnType == ttESCROW_FINISH) && (view.
rules().
enabled(featureSingleAssetVault) || lendingProtocolEnabled);
1438 JLOG(j.
fatal()) <<
"Invariant failed: MPT authorize "
1439 "succeeded but created MPT issuances";
1444 JLOG(j.
fatal()) <<
"Invariant failed: MPT authorize "
1445 "succeeded but deleted issuances";
1450 JLOG(j.
fatal()) <<
"Invariant failed: MPT authorize succeeded "
1451 "but created/deleted bad number mptokens";
1456 JLOG(j.
fatal()) <<
"Invariant failed: MPT authorize submitted by issuer "
1457 "succeeded but created/deleted mptokens";
1465 JLOG(j.
fatal()) <<
"Invariant failed: MPT authorize submitted by holder "
1466 "succeeded but created/deleted bad number of mptokens";
1472 if (txnType == ttESCROW_FINISH)
1477 XRPL_ASSERT_PARTS(!enforceEscrowFinish,
"xrpl::ValidMPTIssuance::finalize",
"not escrow finish tx");
1488 JLOG(j.
fatal()) <<
"Invariant failed: a MPT issuance was created";
1492 JLOG(j.
fatal()) <<
"Invariant failed: a MPT issuance was deleted";
1496 JLOG(j.
fatal()) <<
"Invariant failed: a MPToken was created";
1500 JLOG(j.
fatal()) <<
"Invariant failed: a MPToken was deleted";
1514 if (before && before->getType() != ltPERMISSIONED_DOMAIN)
1516 if (
after &&
after->getType() != ltPERMISSIONED_DOMAIN)
1520 auto const& credentials = sle->getFieldArray(sfAcceptedCredentials);
1523 SleStatus ss{credentials.size(),
false, !sorted.empty(), isDel};
1529 for (
auto const& cred : sorted)
1531 auto const& credTx = credentials[i++];
1532 ss.
isSorted_ = (cred.first == credTx[sfIssuer]) && (cred.second == credTx[sfCredentialType]);
1555 JLOG(j.
fatal()) <<
"Invariant failed: permissioned domain with "
1562 JLOG(j.
fatal()) <<
"Invariant failed: permissioned domain bad "
1570 JLOG(j.
fatal()) <<
"Invariant failed: permissioned domain credentials "
1577 JLOG(j.
fatal()) <<
"Invariant failed: permissioned domain credentials "
1585 if (view.
rules().
enabled(fixPermissionedDomainInvariant))
1595 JLOG(j.
fatal()) <<
"Invariant failed: transaction affected more "
1596 "than 1 permissioned domain entry.";
1602 case ttPERMISSIONED_DOMAIN_SET: {
1605 JLOG(j.
fatal()) <<
"Invariant failed: no domain objects affected by "
1606 "PermissionedDomainSet";
1613 JLOG(j.
fatal()) <<
"Invariant failed: domain object "
1614 "deleted by PermissionedDomainSet";
1617 return check(sleStatus, j);
1619 case ttPERMISSIONED_DOMAIN_DELETE: {
1622 JLOG(j.
fatal()) <<
"Invariant failed: no domain objects affected by "
1623 "PermissionedDomainDelete";
1629 JLOG(j.
fatal()) <<
"Invariant failed: domain object "
1630 "modified, but not deleted by "
1631 "PermissionedDomainDelete";
1640 <<
" domain object(s) affected by an "
1641 "unauthorized transaction. "
1669 if (
after &&
after->getType() == ltACCOUNT_ROOT)
1671 bool const isPseudo = [&]() {
1678 if (
after->at(sfSequence) == 0)
1695 return after->isFieldPresent(*sf);
1700 error <<
"pseudo-account has " << numFields <<
" pseudo-account fields set";
1704 if (before && before->at(sfSequence) !=
after->at(sfSequence))
1712 if (
after->isFieldPresent(sfRegularKey))
1723 bool const enforce = view.
rules().
enabled(featureSingleAssetVault);
1726 "xrpl::ValidPseudoAccounts::finalize : no bad "
1727 "changes or enforce invariant");
1730 for (
auto const& error :
errors_)
1732 JLOG(j.
fatal()) <<
"Invariant failed: " << error;
1750 if (
after->isFieldPresent(sfDomainID))
1756 if (
after->isFieldPresent(sfDomainID))
1764 (!
after->isFieldPresent(sfDomainID) || !
after->isFieldPresent(sfAdditionalBooks) ||
1765 after->getFieldArray(sfAdditionalBooks).size() > 1))
1779 if ((txType != ttPAYMENT && txType != ttOFFER_CREATE) || result !=
tesSUCCESS)
1786 JLOG(j.
fatal()) <<
"Invariant failed: hybrid offer is malformed";
1797 JLOG(j.
fatal()) <<
"Invariant failed: domain doesn't exist";
1807 JLOG(j.
fatal()) <<
"Invariant failed: transaction"
1808 " consumed wrong domains";
1815 JLOG(j.
fatal()) <<
"Invariant failed: domain transaction"
1816 " affected regular offers";
1831 auto const type =
after->getType();
1841 (type == ltACCOUNT_ROOT &&
after->isFieldPresent(sfAMMID)))
1850 if (before->getType() == ltAMM)
1864 bool const positive = amount > beast::zero && amount2 > beast::zero && lptAMMBalance > beast::zero;
1866 return positive || (amount == beast::zero && amount2 == beast::zero && lptAMMBalance == beast::zero);
1894 JLOG(j.
error()) <<
"AMMBid invariant failed: pool changed";
1920 JLOG(j.
error()) <<
"AMMCreate invariant failed: AMM object is not created";
1927 auto const [amount, amount2] =
1935 JLOG(j.
error()) <<
"AMMCreate invariant failed: " << amount <<
" " << amount2 <<
" "
1952 (res ==
tesSUCCESS) ?
"AMM object is not deleted on tesSUCCESS" :
"AMM object is changed on tecINCOMPLETE";
1953 JLOG(j.
error()) <<
"AMMDelete invariant failed: " << msg;
1968 JLOG(j.
error()) <<
"AMM swap invariant failed: AMM object changed";
1984 auto const [amount, amount2] =
1990 auto const poolProductMean =
root2(amount * amount2);
1994 auto weakInvariantCheck = [&]() {
1998 if (!nonNegativeBalances || (!strongInvariantCheck && !weakInvariantCheck()))
2001 <<
" " <<
ammPoolChanged_ <<
" " << amount <<
" " << amount2 <<
" " << poolProductMean <<
" "
2018 JLOG(j.
error()) <<
"AMMDeposit invariant failed: AMM object is deleted";
2062 case ttAMM_CLAWBACK:
2063 case ttAMM_WITHDRAW:
2072 case ttOFFER_CREATE:
2090 if (isDelete || !before)
2105 static auto const fieldChanged = [](
auto const& before,
auto const&
after,
auto const& field) {
2106 bool const beforeField = before->isFieldPresent(field);
2107 bool const afterField =
after->isFieldPresent(field);
2108 return beforeField != afterField || (afterField && before->at(field) !=
after->at(field));
2112 auto const& before = slePair.first;
2113 auto const&
after = slePair.second;
2114 auto const type =
after->getType();
2116 [[maybe_unused]]
bool enforce =
false;
2125 enforce = view.
rules().
enabled(featureLendingProtocol);
2126 bad = fieldChanged(before,
after, sfLedgerEntryType) || fieldChanged(before,
after, sfLedgerIndex) ||
2127 fieldChanged(before,
after, sfSequence) || fieldChanged(before,
after, sfOwnerNode) ||
2128 fieldChanged(before,
after, sfVaultNode) || fieldChanged(before,
after, sfVaultID) ||
2129 fieldChanged(before,
after, sfAccount) || fieldChanged(before,
after, sfOwner) ||
2130 fieldChanged(before,
after, sfManagementFeeRate) ||
2131 fieldChanged(before,
after, sfCoverRateMinimum) ||
2132 fieldChanged(before,
after, sfCoverRateLiquidation);
2140 enforce = view.
rules().
enabled(featureLendingProtocol);
2141 bad = fieldChanged(before,
after, sfLedgerEntryType) || fieldChanged(before,
after, sfLedgerIndex) ||
2142 fieldChanged(before,
after, sfSequence) || fieldChanged(before,
after, sfOwnerNode) ||
2143 fieldChanged(before,
after, sfLoanBrokerNode) || fieldChanged(before,
after, sfLoanBrokerID) ||
2144 fieldChanged(before,
after, sfBorrower) || fieldChanged(before,
after, sfLoanOriginationFee) ||
2145 fieldChanged(before,
after, sfLoanServiceFee) || fieldChanged(before,
after, sfLatePaymentFee) ||
2146 fieldChanged(before,
after, sfClosePaymentFee) || fieldChanged(before,
after, sfOverpaymentFee) ||
2147 fieldChanged(before,
after, sfInterestRate) || fieldChanged(before,
after, sfLateInterestRate) ||
2148 fieldChanged(before,
after, sfCloseInterestRate) ||
2149 fieldChanged(before,
after, sfOverpaymentInterestRate) ||
2150 fieldChanged(before,
after, sfStartDate) || fieldChanged(before,
after, sfPaymentInterval) ||
2151 fieldChanged(before,
after, sfGracePeriod) || fieldChanged(before,
after, sfLoanScale);
2163 enforce = view.
rules().
enabled(featureLendingProtocol);
2164 bad = fieldChanged(before,
after, sfLedgerEntryType) || fieldChanged(before,
after, sfLedgerIndex);
2168 "xrpl::NoModifiedUnmodifiableFields::finalize : no bad "
2169 "changes or enforce invariant");
2190 if (
after->getType() == ltLOAN_BROKER)
2193 broker.brokerBefore = before;
2194 broker.brokerAfter =
after;
2196 else if (
after->getType() == ltACCOUNT_ROOT &&
after->isFieldPresent(sfLoanBrokerID))
2198 auto const& loanBrokerID =
after->at(sfLoanBrokerID);
2202 else if (
after->getType() == ltRIPPLE_STATE)
2206 else if (
after->getType() == ltMPTOKEN)
2216 auto const next = dir->at(~sfIndexNext);
2217 auto const prev = dir->at(~sfIndexPrevious);
2218 if ((prev && *prev) || (next && *next))
2220 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker with zero "
2221 "OwnerCount has multiple directory pages";
2224 auto indexes = dir->getFieldV256(sfIndexes);
2225 if (indexes.size() > 1)
2227 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker with zero "
2228 "OwnerCount has multiple indexes in the Directory root";
2231 if (indexes.size() == 1)
2233 auto const index = indexes.value().front();
2237 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker directory corrupt";
2240 if (sle->getType() != ltRIPPLE_STATE && sle->getType() != ltMPTOKEN)
2242 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker with zero "
2243 "OwnerCount has an unexpected entry in the directory";
2257 for (
auto const& line :
lines_)
2259 for (
auto const& field : {&sfLowLimit, &sfHighLimit})
2265 if (account && account->isFieldPresent(sfLoanBrokerID))
2267 auto const& loanBrokerID = account->at(sfLoanBrokerID);
2273 for (
auto const& mpt :
mpts_)
2279 if (account && account->isFieldPresent(sfLoanBrokerID))
2281 auto const& loanBrokerID = account->at(sfLoanBrokerID);
2287 for (
auto const& [brokerID, broker] :
brokers_)
2293 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker missing";
2297 auto const& before = broker.brokerBefore;
2303 if (
after->at(sfOwnerCount) == 0)
2314 if (before && before->at(sfLoanSequence) >
after->at(sfLoanSequence))
2316 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker sequence number "
2320 if (
after->at(sfDebtTotal) < 0)
2322 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker debt total is negative";
2325 if (
after->at(sfCoverAvailable) < 0)
2327 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker cover available is negative";
2333 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker vault ID is invalid";
2336 auto const& vaultAsset = vault->at(sfAsset);
2339 after->at(sfAccount),
2345 JLOG(j.
fatal()) <<
"Invariant failed: Loan Broker cover available "
2346 "is less than pseudo-account asset balance";
2374 if (
after->at(sfPaymentRemaining) == 0 &&
2375 (
after->at(sfTotalValueOutstanding) != beast::zero ||
after->at(sfPrincipalOutstanding) != beast::zero ||
2376 after->at(sfManagementFeeOutstanding) != beast::zero))
2378 JLOG(j.
fatal()) <<
"Invariant failed: Loan with zero payments "
2379 "remaining has not been paid off";
2384 if (
after->at(sfPaymentRemaining) != 0 &&
after->at(sfTotalValueOutstanding) == beast::zero &&
2385 after->at(sfPrincipalOutstanding) == beast::zero &&
after->at(sfManagementFeeOutstanding) == beast::zero)
2387 JLOG(j.
fatal()) <<
"Invariant failed: Loan with zero payments "
2388 "remaining has not been paid off";
2393 JLOG(j.
fatal()) <<
"Invariant failed: Loan Overpayment flag changed";
2397 for (
auto const field :
2401 &sfPrincipalOutstanding,
2402 &sfTotalValueOutstanding,
2403 &sfManagementFeeOutstanding})
2405 if (
after->at(*field) < 0)
2407 JLOG(j.
fatal()) <<
"Invariant failed: " << field->getName() <<
" is negative ";
2412 for (
auto const field : {
2416 if (
after->at(*field) <= 0)
2418 JLOG(j.
fatal()) <<
"Invariant failed: " << field->getName() <<
" is zero or negative ";
2429 XRPL_ASSERT(from.
getType() == ltVAULT,
"ValidVault::Vault::make : from Vault object");
2433 self.
asset = from.
at(sfAsset);
2435 self.
owner = from.
at(sfOwner);
2447 XRPL_ASSERT(from.
getType() == ltMPTOKEN_ISSUANCE,
"ValidVault::Shares::make : from MPTokenIssuance object");
2463 after !=
nullptr && (before !=
nullptr || !isDelete),
2464 "xrpl::ValidVault::visitEntry : some object is available");
2475 switch (before->getType())
2480 case ltMPTOKEN_ISSUANCE:
2484 balanceDelta =
static_cast<std::int64_t>(before->getFieldU64(sfOutstandingAmount));
2488 balanceDelta =
static_cast<std::int64_t>(before->getFieldU64(sfMPTAmount));
2491 case ltACCOUNT_ROOT:
2492 case ltRIPPLE_STATE:
2493 balanceDelta = before->getFieldAmount(sfBalance);
2500 if (!isDelete &&
after)
2502 switch (
after->getType())
2507 case ltMPTOKEN_ISSUANCE:
2518 case ltACCOUNT_ROOT:
2519 case ltRIPPLE_STATE:
2520 balanceDelta -=
Number(
after->getFieldAmount(sfBalance));
2527 uint256 const key = (before ? before->key() :
after->key());
2540 bool const enforce = view.
rules().
enabled(featureSingleAssetVault);
2550 "Invariant failed: vault operation succeeded without modifying "
2552 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault noop invariant");
2561 "Invariant failed: vault updated by a wrong transaction type";
2564 "xrpl::ValidVault::finalize : illegal vault transaction "
2572 "Invariant failed: vault operation updated more than single vault";
2573 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : single vault invariant");
2583 if (txnType != ttVAULT_DELETE)
2586 "Invariant failed: vault deleted by a wrong transaction type";
2589 "xrpl::ValidVault::finalize : illegal vault deletion "
2604 if (e.share.getMptID() == beforeVault.shareMPTID)
2605 return std::move(e);
2612 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must also "
2614 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : shares deletion invariant");
2619 if (deletedShares->sharesTotal != 0)
2621 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must have no "
2622 "shares outstanding";
2625 if (beforeVault.assetsTotal !=
zero)
2627 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must have no "
2628 "assets outstanding";
2631 if (beforeVault.assetsAvailable !=
zero)
2633 JLOG(j.
fatal()) <<
"Invariant failed: deleted vault must have no "
2640 else if (txnType == ttVAULT_DELETE)
2642 JLOG(j.
fatal()) <<
"Invariant failed: vault deletion succeeded without "
2644 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault deletion invariant");
2652 "xrpl::ValidVault::finalize : single vault operation");
2662 if (e.share.getMptID() == afterVault.shareMPTID)
2677 if (afterVault.asset != beforeVault.asset || afterVault.pseudoId != beforeVault.pseudoId ||
2678 afterVault.shareMPTID != beforeVault.shareMPTID)
2680 JLOG(j.
fatal()) <<
"Invariant failed: violation of vault immutable data";
2687 JLOG(j.
fatal()) <<
"Invariant failed: updated vault must have shares";
2688 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault has shares invariant");
2692 if (updatedShares->sharesTotal == 0)
2694 if (afterVault.assetsTotal !=
zero)
2696 JLOG(j.
fatal()) <<
"Invariant failed: updated zero sized "
2697 "vault must have no assets outstanding";
2700 if (afterVault.assetsAvailable !=
zero)
2702 JLOG(j.
fatal()) <<
"Invariant failed: updated zero sized "
2703 "vault must have no assets available";
2707 else if (updatedShares->sharesTotal > updatedShares->sharesMaximum)
2710 <<
"Invariant failed: updated shares must not exceed maximum " << updatedShares->sharesMaximum;
2714 if (afterVault.assetsAvailable <
zero)
2716 JLOG(j.
fatal()) <<
"Invariant failed: assets available must be positive";
2720 if (afterVault.assetsAvailable > afterVault.assetsTotal)
2722 JLOG(j.
fatal()) <<
"Invariant failed: assets available must "
2723 "not be greater than assets outstanding";
2726 else if (afterVault.lossUnrealized > afterVault.assetsTotal - afterVault.assetsAvailable)
2729 <<
"Invariant failed: loss unrealized must not exceed "
2730 "the difference between assets outstanding and available";
2734 if (afterVault.assetsTotal <
zero)
2736 JLOG(j.
fatal()) <<
"Invariant failed: assets outstanding must be positive";
2740 if (afterVault.assetsMaximum <
zero)
2742 JLOG(j.
fatal()) <<
"Invariant failed: assets maximum must be positive";
2751 "Invariant failed: vault created by a wrong transaction type";
2752 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault creation invariant");
2757 txnType != ttLOAN_MANAGE && txnType != ttLOAN_PAY)
2760 "Invariant failed: vault transaction must not change loss "
2772 if (e.share.getMptID() == beforeVault.shareMPTID)
2773 return std::move(e);
2778 if (!beforeShares &&
2783 JLOG(j.
fatal()) <<
"Invariant failed: vault operation succeeded "
2784 "without updating shares";
2785 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : shares noop invariant");
2789 auto const& vaultAsset = afterVault.asset;
2796 return it->second *
sign;
2800 [&]<
typename TIss>(TIss
const& issue) {
2812 vaultAsset.value());
2815 auto ret = deltaAssets(tx[sfAccount]);
2817 if (!ret.has_value() || !vaultAsset.native())
2821 if (
auto const delegate = tx[~sfDelegate]; delegate.has_value() && *delegate != tx[sfAccount])
2824 *ret += fee.
drops();
2831 auto const it = [&]() {
2832 if (
id == afterVault.pseudoId)
2840 auto const vaultHoldsNoAssets = [&](
Vault const& vault) {
2841 return vault.assetsAvailable == 0 && vault.assetsTotal == 0;
2852 case ttVAULT_CREATE: {
2858 <<
"Invariant failed: create operation must not have "
2863 if (afterVault.assetsAvailable !=
zero || afterVault.assetsTotal !=
zero ||
2864 afterVault.lossUnrealized !=
zero || updatedShares->sharesTotal != 0)
2867 <<
"Invariant failed: created vault must be empty";
2871 if (afterVault.pseudoId != updatedShares->share.getIssuer())
2874 <<
"Invariant failed: shares issuer and vault "
2875 "pseudo-account must be the same";
2879 auto const sleSharesIssuer = view.
read(
keylet::account(updatedShares->share.getIssuer()));
2880 if (!sleSharesIssuer)
2883 <<
"Invariant failed: shares issuer must exist";
2890 <<
"Invariant failed: shares issuer must be a "
2895 if (
auto const vaultId = (*sleSharesIssuer)[~sfVaultID]; !vaultId || *vaultId != afterVault.key)
2898 <<
"Invariant failed: shares issuer pseudo-account "
2899 "must point back to the vault";
2908 XRPL_ASSERT(!
beforeVault_.empty(),
"xrpl::ValidVault::finalize : set updated a vault");
2911 auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
2912 if (vaultDeltaAssets)
2915 "Invariant failed: set must not change vault balance";
2919 if (beforeVault.assetsTotal != afterVault.assetsTotal)
2922 "Invariant failed: set must not change assets "
2927 if (afterVault.assetsMaximum >
zero && afterVault.assetsTotal > afterVault.assetsMaximum)
2930 "Invariant failed: set assets outstanding must not "
2931 "exceed assets maximum";
2935 if (beforeVault.assetsAvailable != afterVault.assetsAvailable)
2938 "Invariant failed: set must not change assets "
2943 if (beforeShares && updatedShares && beforeShares->sharesTotal != updatedShares->sharesTotal)
2946 "Invariant failed: set must not change shares "
2953 case ttVAULT_DEPOSIT: {
2956 XRPL_ASSERT(!
beforeVault_.empty(),
"xrpl::ValidVault::finalize : deposit updated a vault");
2959 auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
2961 if (!vaultDeltaAssets)
2964 "Invariant failed: deposit must change vault balance";
2968 if (*vaultDeltaAssets > tx[sfAmount])
2971 "Invariant failed: deposit must not change vault "
2972 "balance by more than deposited amount";
2976 if (*vaultDeltaAssets <=
zero)
2979 "Invariant failed: deposit must increase vault balance";
2985 bool const issuerDeposit = [&]() ->
bool {
2986 if (vaultAsset.native())
2988 return tx[sfAccount] == vaultAsset.getIssuer();
2993 auto const accountDeltaAssets = deltaAssetsTxAccount();
2994 if (!accountDeltaAssets)
2997 "Invariant failed: deposit must change depositor "
3002 if (*accountDeltaAssets >=
zero)
3005 "Invariant failed: deposit must decrease depositor "
3010 if (*accountDeltaAssets * -1 != *vaultDeltaAssets)
3013 "Invariant failed: deposit must change vault and "
3014 "depositor balance by equal amount";
3019 if (afterVault.assetsMaximum >
zero && afterVault.assetsTotal > afterVault.assetsMaximum)
3022 "Invariant failed: deposit assets outstanding must not "
3023 "exceed assets maximum";
3027 auto const accountDeltaShares = deltaShares(tx[sfAccount]);
3028 if (!accountDeltaShares)
3031 "Invariant failed: deposit must change depositor "
3036 if (*accountDeltaShares <=
zero)
3039 "Invariant failed: deposit must increase depositor "
3044 auto const vaultDeltaShares = deltaShares(afterVault.pseudoId);
3045 if (!vaultDeltaShares || *vaultDeltaShares ==
zero)
3048 "Invariant failed: deposit must change vault shares";
3052 if (*vaultDeltaShares * -1 != *accountDeltaShares)
3055 "Invariant failed: deposit must change depositor and "
3056 "vault shares by equal amount";
3060 if (beforeVault.assetsTotal + *vaultDeltaAssets != afterVault.assetsTotal)
3062 JLOG(j.
fatal()) <<
"Invariant failed: deposit and assets "
3063 "outstanding must add up";
3066 if (beforeVault.assetsAvailable + *vaultDeltaAssets != afterVault.assetsAvailable)
3068 JLOG(j.
fatal()) <<
"Invariant failed: deposit and assets "
3069 "available must add up";
3075 case ttVAULT_WITHDRAW: {
3080 "xrpl::ValidVault::finalize : withdrawal updated a "
3084 auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
3086 if (!vaultDeltaAssets)
3088 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal must "
3089 "change vault balance";
3093 if (*vaultDeltaAssets >=
zero)
3095 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal must "
3096 "decrease vault balance";
3102 bool const issuerWithdrawal = [&]() ->
bool {
3103 if (vaultAsset.native())
3105 auto const destination = tx[~sfDestination].value_or(tx[sfAccount]);
3106 return destination == vaultAsset.getIssuer();
3109 if (!issuerWithdrawal)
3111 auto const accountDeltaAssets = deltaAssetsTxAccount();
3113 if (
auto const destination = tx[~sfDestination];
3114 destination && *destination != tx[sfAccount])
3115 return deltaAssets(*destination);
3119 if (accountDeltaAssets.has_value() == otherAccountDelta.has_value())
3122 "Invariant failed: withdrawal must change one "
3123 "destination balance";
3127 auto const destinationDelta =
3128 accountDeltaAssets ? *accountDeltaAssets : *otherAccountDelta;
3130 if (destinationDelta <=
zero)
3133 "Invariant failed: withdrawal must increase "
3134 "destination balance";
3138 if (*vaultDeltaAssets * -1 != destinationDelta)
3141 "Invariant failed: withdrawal must change vault "
3142 "and destination balance by equal amount";
3147 auto const accountDeltaShares = deltaShares(tx[sfAccount]);
3148 if (!accountDeltaShares)
3151 "Invariant failed: withdrawal must change depositor "
3156 if (*accountDeltaShares >=
zero)
3159 "Invariant failed: withdrawal must decrease depositor "
3164 auto const vaultDeltaShares = deltaShares(afterVault.pseudoId);
3165 if (!vaultDeltaShares || *vaultDeltaShares ==
zero)
3168 "Invariant failed: withdrawal must change vault shares";
3172 if (*vaultDeltaShares * -1 != *accountDeltaShares)
3175 "Invariant failed: withdrawal must change depositor "
3176 "and vault shares by equal amount";
3181 if (beforeVault.assetsTotal + *vaultDeltaAssets != afterVault.assetsTotal)
3183 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal and "
3184 "assets outstanding must add up";
3188 if (beforeVault.assetsAvailable + *vaultDeltaAssets != afterVault.assetsAvailable)
3190 JLOG(j.
fatal()) <<
"Invariant failed: withdrawal and "
3191 "assets available must add up";
3197 case ttVAULT_CLAWBACK: {
3200 XRPL_ASSERT(!
beforeVault_.empty(),
"xrpl::ValidVault::finalize : clawback updated a vault");
3203 if (vaultAsset.native() || vaultAsset.getIssuer() != tx[sfAccount])
3207 if (!(beforeShares && beforeShares->sharesTotal > 0 && vaultHoldsNoAssets(beforeVault) &&
3208 beforeVault.owner == tx[sfAccount]))
3211 "Invariant failed: clawback may only be performed "
3212 "by the asset issuer, or by the vault owner of an "
3218 auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
3219 if (vaultDeltaAssets)
3221 if (*vaultDeltaAssets >=
zero)
3224 "Invariant failed: clawback must decrease vault "
3229 if (beforeVault.assetsTotal + *vaultDeltaAssets != afterVault.assetsTotal)
3232 "Invariant failed: clawback and assets outstanding "
3237 if (beforeVault.assetsAvailable + *vaultDeltaAssets != afterVault.assetsAvailable)
3240 "Invariant failed: clawback and assets available "
3245 else if (!vaultHoldsNoAssets(beforeVault))
3248 "Invariant failed: clawback must change vault balance";
3252 auto const accountDeltaShares = deltaShares(tx[sfHolder]);
3253 if (!accountDeltaShares)
3256 "Invariant failed: clawback must change holder shares";
3260 if (*accountDeltaShares >=
zero)
3263 "Invariant failed: clawback must decrease holder "
3268 auto const vaultDeltaShares = deltaShares(afterVault.pseudoId);
3269 if (!vaultDeltaShares || *vaultDeltaShares ==
zero)
3272 "Invariant failed: clawback must change vault shares";
3276 if (*vaultDeltaShares * -1 != *accountDeltaShares)
3279 "Invariant failed: clawback must change holder and "
3280 "vault shares by equal amount";
3296 UNREACHABLE(
"xrpl::ValidVault::finalize : unknown transaction type");
3306 XRPL_ASSERT(enforce,
"xrpl::ValidVault::finalize : vault invariants");
A generic endpoint for log messages.
std::vector< std::pair< std::shared_ptr< SLE const >, std::shared_ptr< SLE const > > > accountsDeleted_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::uint32_t accountsDeleted_
A currency issued by an account.
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::uint32_t beforeMintedTotal
std::uint32_t afterBurnedTotal
std::uint32_t afterMintedTotal
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::uint32_t beforeBurnedTotal
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
bool deepFreezeWithoutFreeze_
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::set< std::pair< SLE::const_pointer, SLE::const_pointer > > changedEntries_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
Number is a floating point type that can represent a wide range of values.
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
virtual std::optional< key_type > succ(key_type const &key, std::optional< key_type > const &last=std::nullopt) const =0
Return the key of the next state item.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
int signum() const noexcept
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
bool native() const noexcept
uint256 const & key() const
Returns the 'key' (or 'index') of this item.
LedgerEntryType getType() const
uint192 getFieldH192(SField const &field) const
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
std::uint32_t getFieldU32(SField const &field) const
uint256 getHash(HashPrefix prefix) const
bool isFieldPresent(SField const &field) const
uint256 getFieldH256(SField const &field) const
AccountID getAccountID(SField const &field) const
STAmount const & getFieldAmount(SField const &field) const
TxType getTxnType() const
uint256 getTransactionID() const
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::shared_ptr< SLE const > findIssuer(AccountID const &issuerID, ReadView const &view)
void recordBalance(Issue const &issue, BalanceChange change)
bool isValidEntry(std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)
bool validateIssuerChanges(std::shared_ptr< SLE const > const &issuer, IssuerChanges const &changes, STTx const &tx, beast::Journal const &j, bool enforce)
std::map< AccountID, std::shared_ptr< SLE const > const > possibleIssuers_
STAmount calculateBalanceChange(std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after, bool isDelete)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
bool validateFrozenState(BalanceChange const &change, bool high, STTx const &tx, beast::Journal const &j, bool enforce, bool globalFreeze)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
void recordBalanceChanges(std::shared_ptr< SLE const > const &after, STAmount const &balanceChange)
std::optional< AccountID > ammAccount_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
bool finalizeWithdraw(STTx const &, ReadView const &, bool enforce, beast::Journal const &) const
bool finalizeBid(bool enforce, beast::Journal const &) const
bool finalizeCreate(STTx const &, ReadView const &, bool enforce, beast::Journal const &) const
bool finalizeDEX(bool enforce, beast::Journal const &) const
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalizeDeposit(STTx const &, ReadView const &, bool enforce, beast::Journal const &) const
bool generalInvariant(STTx const &, ReadView const &, ZeroAllowed zeroAllowed, beast::Journal const &) const
std::optional< STAmount > lptAMMBalanceAfter_
bool finalizeVote(bool enforce, beast::Journal const &) const
bool finalizeDelete(bool enforce, TER res, beast::Journal const &) const
std::optional< STAmount > lptAMMBalanceBefore_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::uint32_t trustlinesChanged
std::uint32_t mptokensChanged
std::map< uint256, BrokerInfo > brokers_
bool goodZeroDirectory(ReadView const &view, SLE::const_ref dir, beast::Journal const &j) const
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::vector< SLE::const_pointer > mpts_
std::vector< SLE::const_pointer > lines_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::vector< std::pair< SLE::const_pointer, SLE::const_pointer > > loans_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::uint32_t mptokensCreated_
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::uint32_t mptokensDeleted_
std::uint32_t mptIssuancesCreated_
std::uint32_t mptIssuancesDeleted_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::uint32_t accountsCreated_
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::uint32_t accountSeq_
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
hash_set< uint256 > domains_
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::vector< SleStatus > sleStatus_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
std::vector< std::string > errors_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
static Number constexpr zero
std::vector< Shares > afterMPTs_
std::unordered_map< uint256, Number > deltas_
std::vector< Vault > afterVault_
std::vector< Shares > beforeMPTs_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::vector< Vault > beforeVault_
constexpr value_type drops() const
Returns the number of drops.
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, std::shared_ptr< SLE const > const &, std::shared_ptr< SLE const > const &)
T emplace_back(T... args)
std::set< std::pair< AccountID, Slice > > makeSorted(STArray const &credentials)
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Keylet loanbroker(AccountID const &owner, std::uint32_t seq) noexcept
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
uint256 constexpr pageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
bool compareTokens(uint256 const &a, uint256 const &b)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::size_t constexpr dirMaxTokensPerPage
The maximum number of items in an NFT page.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
std::vector< SField const * > const & getPseudoAccountFields()
bool isXRP(AccountID const &c)
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
constexpr base_uint< Bits, Tag > operator|(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
static bool validBalances(STAmount const &amount, STAmount const &amount2, STAmount const &lptAMMBalance, ValidAMM::ZeroAllowed zeroAllowed)
bool hasPrivilege(STTx const &tx, Privilege priv)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
std::size_t constexpr maxPermissionedDomainCredentialsArraySize
The maximum number of credentials can be passed in array for permissioned domain.
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue1, Issue const &issue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool balances.
@ transactionID
transaction plus signature to give transaction ID
bool isTesSuccess(TER x) noexcept
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
constexpr std::enable_if_t< std::is_integral_v< Dest > &&std::is_integral_v< Src >, Dest > safe_cast(Src s) noexcept
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
std::array< keyletDesc< AccountID const & >, 6 > const directAccountKeylets
A pair of SHAMap key and LedgerEntryType.
int const balanceChangeSign
std::shared_ptr< SLE const > const line
std::vector< BalanceChange > senders
std::vector< BalanceChange > receivers
std::size_t credentialsSize_
std::uint64_t sharesMaximum
static Shares make(SLE const &)
std::uint64_t sharesTotal
static Vault make(SLE const &)