1#include <xrpl/tx/invariants/InvariantCheck.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.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/ledger/ReadView.h>
9#include <xrpl/ledger/helpers/AccountRootHelpers.h>
10#include <xrpl/ledger/helpers/TokenHelpers.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/Feature.h>
13#include <xrpl/protocol/Indexes.h>
14#include <xrpl/protocol/Issue.h>
15#include <xrpl/protocol/Keylet.h>
16#include <xrpl/protocol/LedgerFormats.h>
17#include <xrpl/protocol/MPTIssue.h>
18#include <xrpl/protocol/Protocol.h>
19#include <xrpl/protocol/Rules.h>
20#include <xrpl/protocol/SField.h>
21#include <xrpl/protocol/STAmount.h>
22#include <xrpl/protocol/STLedgerEntry.h>
23#include <xrpl/protocol/STNumber.h>
24#include <xrpl/protocol/STTx.h>
25#include <xrpl/protocol/SystemParameters.h>
26#include <xrpl/protocol/TER.h>
27#include <xrpl/protocol/TxFormats.h>
28#include <xrpl/protocol/UintTypes.h>
29#include <xrpl/protocol/XRPAmount.h>
30#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
43#pragma push_macro("TRANSACTION")
46#define TRANSACTION(tag, value, name, delegable, amendment, privileges, ...) \
48 return (privileges) & priv; \
56#include <xrpl/protocol/detail/transactions.macro>
65#pragma pop_macro("TRANSACTION")
77 UNREACHABLE(
"xrpl::ledgerEntryTypeName : ledger entry has no known ledger format");
81 return item->getName();
101 JLOG(j.
fatal()) <<
"Invariant failed: fee paid was negative: " << fee.
drops();
109 JLOG(j.
fatal()) <<
"Invariant failed: fee paid exceeds system limit: " << fee.
drops();
117 JLOG(j.
fatal()) <<
"Invariant failed: fee paid is " << fee.
drops()
118 <<
" exceeds fee specified in transaction.";
139 switch (before->getType())
142 drops_ -= (*before)[sfBalance].xrp().drops();
145 drops_ -= ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops();
148 if (
isXRP((*before)[sfAmount]))
149 drops_ -= (*before)[sfAmount].xrp().drops();
152 if (before->isFieldPresent(sfFeeAmount))
155 isXRP((*before)[sfFeeAmount]),
156 "XRPNotCreated::visitEntry : Sponsorship.FeeAmount is XRP");
157 drops_ -= (*before)[sfFeeAmount].xrp().drops();
168 UNREACHABLE(
"xrpl::XRPNotCreated::visitEntry : after can't be null");
172 switch (
after->getType())
175 drops_ += (*after)[sfBalance].xrp().drops();
179 drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
183 drops_ += (*after)[sfAmount].xrp().drops();
186 if (!isDelete &&
after->isFieldPresent(sfFeeAmount))
190 "XRPNotCreated::visitEntry : Sponsorship.FeeAmount is XRP");
191 drops_ += (*after)[sfFeeAmount].xrp().drops();
211 JLOG(j.
fatal()) <<
"Invariant failed: XRP net change was positive: " <<
drops_;
218 JLOG(j.
fatal()) <<
"Invariant failed: XRP net change of " <<
drops_ <<
" doesn't match fee "
231 auto isBad = [](
STAmount const& balance) {
232 if (!balance.native())
235 auto const drops = balance.xrp();
249 if (before && before->getType() == ltACCOUNT_ROOT)
250 bad_ |= isBad((*before)[sfBalance]);
252 if (
after &&
after->getType() == ltACCOUNT_ROOT)
266 JLOG(j.
fatal()) <<
"Invariant failed: incorrect account XRP balance";
287 return pays.
native() && gets.native();
290 if (before && before->getType() == ltOFFER)
291 bad_ |= isBad((*before)[sfTakerPays], (*before)[sfTakerGets]);
307 JLOG(j.
fatal()) <<
"Invariant failed: offer with a bad amount";
319 auto isBad = [](
STAmount const& amount) {
331 return amount.asset().visit(
332 [&](
Issue const& issue) {
349 if (amount.mpt() >
MPTAmount{kMaxMpTokenAmount})
358 if (before && before->getType() == ltESCROW)
359 bad_ |= isBad((*before)[sfAmount]);
371 if (
after &&
after->getType() == ltMPTOKEN_ISSUANCE)
373 auto const outstanding = (*after)[sfOutstandingAmount];
374 checkAmount(outstanding);
375 if (
auto const locked = (*
after)[~sfLockedAmount])
377 checkAmount(*locked);
378 bool const isBad = outstanding < *locked;
379 if (overwriteFixEnabled)
392 auto const mptAmount = (*after)[sfMPTAmount];
393 checkAmount(mptAmount);
394 if (
auto const locked = (*
after)[~sfLockedAmount])
396 checkAmount(*locked);
411 JLOG(j.
fatal()) <<
"Invariant failed: escrow specifies invalid amount";
423 if (isDelete && before && before->getType() == ltACCOUNT_ROOT)
446 JLOG(j.
fatal()) <<
"Invariant failed: account deletion "
447 "succeeded without deleting an account";
451 JLOG(j.
fatal()) <<
"Invariant failed: account deletion "
452 "succeeded but deleted multiple accounts!";
466 JLOG(j.
fatal()) <<
"Invariant failed: an account root was deleted";
475 if (isDelete && before && before->getType() == ltACCOUNT_ROOT)
491 [[maybe_unused]]
bool const enforce = view.
rules().
enabled(fixCleanup3_2_0) ||
495 auto const objectExists = [&view, enforce, &j](
auto const&
keylet) {
500 JLOG(j.
fatal()) <<
"Invariant failed: account deletion left behind a "
506 "xrpl::AccountRootsDeletedClean::finalize::objectExists : "
507 "account deletion left no objects behind");
515 auto const accountID = before->getAccountID(sfAccount);
519 JLOG(j.
fatal()) <<
"Invariant failed: account deletion left "
520 "behind a non-zero balance";
523 "xrpl::AccountRootsDeletedClean::finalize : "
524 "deleted account has zero balance");
529 if (
after->at(sfOwnerCount) != 0)
531 JLOG(j.
fatal()) <<
"Invariant failed: account deletion left "
532 "behind a non-zero owner count";
535 "xrpl::AccountRootsDeletedClean::finalize : "
536 "deleted account has zero owner count");
541 if (
after->isFieldPresent(sfSponsoredOwnerCount) ||
542 after->isFieldPresent(sfSponsoringOwnerCount) ||
543 after->isFieldPresent(sfSponsoringAccountCount) ||
after->isFieldPresent(sfSponsor))
545 JLOG(j.
fatal()) <<
"Invariant failed: account deletion left "
546 "behind a sponsorship field";
549 "xrpl::AccountRootsDeletedClean::finalize : "
550 "deleted account has no sponsorship fields");
558 if (objectExists(
std::invoke(keyletfunc, accountID)) && enforce)
573 if (key && objectExists(
Keylet{ltNFTOKEN_PAGE, *key}) && enforce)
581 if (before->isFieldPresent(*field))
583 auto const key = before->getFieldH256(*field);
598 if (before &&
after && before->getType() !=
after->getType())
603#pragma push_macro("LEDGER_ENTRY")
606#define LEDGER_ENTRY(tag, ...) case tag:
608 switch (
after->getType())
610#include <xrpl/protocol/detail/ledger_entries.macro>
619#pragma pop_macro("LEDGER_ENTRY")
636 JLOG(j.
fatal()) <<
"Invariant failed: ledger entry type mismatch";
641 JLOG(j.
fatal()) <<
"Invariant failed: invalid ledger entry type added";
654 if (
after &&
after->getType() == ltRIPPLE_STATE)
659 bool const isXrp =
after->getFieldAmount(sfLowLimit).asset() ==
xrpIssue() ||
661 if (overwriteFixEnabled)
683 JLOG(j.
fatal()) <<
"Invariant failed: an XRP trust line was created";
692 if (
after &&
after->getType() == ltRIPPLE_STATE)
696 bool const lowFreeze =
after->isFlag(lsfLowFreeze);
697 bool const lowDeepFreeze =
after->isFlag(lsfLowDeepFreeze);
699 bool const highFreeze =
after->isFlag(lsfHighFreeze);
700 bool const highDeepFreeze =
after->isFlag(lsfHighDeepFreeze);
702 bool const bad = (lowDeepFreeze && !lowFreeze) || (highDeepFreeze && !highFreeze);
703 if (overwriteFixEnabled)
725 JLOG(j.
fatal()) <<
"Invariant failed: a trust line with deep freeze flag "
726 "without normal freeze was created";
735 if (!before &&
after->getType() == ltACCOUNT_ROOT)
757 JLOG(j.
fatal()) <<
"Invariant failed: multiple accounts "
758 "created in a single transaction";
765 bool const pseudoAccount =
767 (view.
rules().enabled(featureSingleAssetVault) ||
772 JLOG(j.
fatal()) <<
"Invariant failed: pseudo-account created by a "
773 "wrong transaction type";
781 JLOG(j.
fatal()) <<
"Invariant failed: account created with "
782 "wrong starting sequence number";
788 std::uint32_t const expected = (lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
791 JLOG(j.
fatal()) <<
"Invariant failed: pseudo-account created with "
800 JLOG(j.
fatal()) <<
"Invariant failed: account root created illegally";
816 if (sle->getType() != ltRIPPLE_STATE ||
822 STAmount balance = sle->getFieldAmount(sfBalance);
825 balance.
get<
Issue>().account = issuer;
832 if (before && before->getType() == ltRIPPLE_STATE)
835 iou_.before = before;
838 if (!isDelete &&
after &&
after->getType() == ltRIPPLE_STATE)
841 if (before && before->getType() == ltMPTOKEN)
844 mpt_.before = before;
847 if (!isDelete &&
after &&
after->getType() == ltMPTOKEN)
866 JLOG(j.
fatal()) <<
"Invariant failed: more than one trustline changed.";
872 JLOG(j.
fatal()) <<
"Invariant failed: more than one mptokens changed.";
876 bool const mptV2Enabled = view.
rules().
enabled(featureMPTokensV2);
879 JLOG(j.
fatal()) <<
"Invariant failed: trustline and MPToken both changed.";
889 [&](
Issue const& issue) {
895 if (holderBalance.
signum() < 0)
897 JLOG(j.
fatal()) <<
"Invariant failed: trustline or MPT balance is negative";
904 <<
"Invariant failed: trustline clawback changed the wrong line";
905 return !mptV2Enabled;
912 if (!beforeBalance || !afterBalance)
915 <<
"Invariant failed: trustline clawback changed the wrong line";
916 return !mptV2Enabled;
920 clawAmount.
get<
Issue>().account = issuer;
923 JLOG(j.
fatal()) <<
"Invariant failed: trustline clawback amount is invalid";
924 return !mptV2Enabled;
927 if (*afterBalance > *beforeBalance ||
928 (*beforeBalance - *afterBalance) !=
std::min(*beforeBalance, clawAmount))
931 <<
"Invariant failed: trustline clawback balance change is invalid";
932 return !mptV2Enabled;
938 auto const holder = tx[~sfHolder];
941 JLOG(j.
fatal()) <<
"Invariant failed: MPT clawback missing holder";
942 return !mptV2Enabled;
947 JLOG(j.
fatal()) <<
"Invariant failed: MPT clawback token is missing";
948 return !mptV2Enabled;
951 if (
mpt_.before->getAccountID(sfAccount) != *holder ||
952 mpt_.after->getAccountID(sfAccount) != *holder ||
953 (*
mpt_.before)[sfMPTokenIssuanceID] != issue.getMptID() ||
954 (*
mpt_.after)[sfMPTokenIssuanceID] != issue.getMptID())
956 JLOG(j.
fatal()) <<
"Invariant failed: MPT clawback changed the wrong token";
957 return !mptV2Enabled;
960 auto const before =
mpt_.before->getFieldU64(sfMPTAmount);
961 auto const after =
mpt_.after->getFieldU64(sfMPTAmount);
964 JLOG(j.
fatal()) <<
"Invariant failed: MPT clawback amount is invalid";
965 return !mptV2Enabled;
967 auto const clawAmount = amount.
mantissa();
974 <<
"Invariant failed: MPT clawback balance change is invalid";
975 return !mptV2Enabled;
986 JLOG(j.
fatal()) <<
"Invariant failed: some trustlines were changed "
987 "despite failure of the transaction.";
993 JLOG(j.
fatal()) <<
"Invariant failed: some mptokens were changed "
994 "despite failure of the transaction.";
1013 if (
after &&
after->getType() == ltACCOUNT_ROOT)
1015 bool const isPseudo = [&]() {
1022 if (
after->at(sfSequence) == 0)
1042 [&
after](
SField const* sf) ->
bool {
return after->isFieldPresent(*sf); });
1046 error <<
"pseudo-account has " << numFields <<
" pseudo-account fields set";
1050 if (before && before->at(sfSequence) !=
after->at(sfSequence))
1052 errors_.emplace_back(
"pseudo-account sequence changed");
1054 if (!
after->isFlag(lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth))
1056 errors_.emplace_back(
"pseudo-account flags are not set");
1058 if (
after->isFieldPresent(sfRegularKey))
1060 errors_.emplace_back(
"pseudo-account has a regular key");
1062 if (
after->isFieldPresent(sfSponsoredOwnerCount) ||
1063 after->isFieldPresent(sfSponsoringOwnerCount) ||
after->isFieldPresent(sfSponsor) ||
1064 after->isFieldPresent(sfSponsoringAccountCount))
1066 errors_.emplace_back(
"pseudo-account has a sponsorship field");
1080 bool const enforce = view.
rules().
enabled(featureSingleAssetVault);
1083 "xrpl::ValidPseudoAccounts::finalize : no bad "
1084 "changes or enforce invariant");
1087 for (
auto const& error :
errors_)
1089 JLOG(j.
fatal()) <<
"Invariant failed: " << error;
1102 if (isDelete || !before)
1119 static auto const kFieldChanged = [](
auto const& before,
auto const&
after,
auto const& field) {
1120 bool const beforeField = before->isFieldPresent(field);
1121 bool const afterField =
after->isFieldPresent(field);
1122 return beforeField != afterField || (afterField && before->at(field) !=
after->at(field));
1126 auto const& before = slePair.first;
1127 auto const&
after = slePair.second;
1128 auto const type =
after->getType();
1133 bool const enforce = view.
rules().
enabled(featureLendingProtocol);
1134 bool bad = kFieldChanged(before,
after, sfLedgerEntryType) ||
1135 kFieldChanged(before,
after, sfLedgerIndex);
1139 bad = bad || kFieldChanged(before,
after, sfSequence) ||
1140 kFieldChanged(before,
after, sfOwnerNode) ||
1141 kFieldChanged(before,
after, sfVaultNode) ||
1142 kFieldChanged(before,
after, sfVaultID) ||
1143 kFieldChanged(before,
after, sfAccount) ||
1144 kFieldChanged(before,
after, sfOwner) ||
1145 kFieldChanged(before,
after, sfManagementFeeRate) ||
1146 kFieldChanged(before,
after, sfCoverRateMinimum) ||
1147 kFieldChanged(before,
after, sfCoverRateLiquidation);
1150 bad = bad || kFieldChanged(before,
after, sfSequence) ||
1151 kFieldChanged(before,
after, sfOwnerNode) ||
1152 kFieldChanged(before,
after, sfLoanBrokerNode) ||
1153 kFieldChanged(before,
after, sfLoanBrokerID) ||
1154 kFieldChanged(before,
after, sfBorrower) ||
1155 kFieldChanged(before,
after, sfLoanOriginationFee) ||
1156 kFieldChanged(before,
after, sfLoanServiceFee) ||
1157 kFieldChanged(before,
after, sfLatePaymentFee) ||
1158 kFieldChanged(before,
after, sfClosePaymentFee) ||
1159 kFieldChanged(before,
after, sfOverpaymentFee) ||
1160 kFieldChanged(before,
after, sfInterestRate) ||
1161 kFieldChanged(before,
after, sfLateInterestRate) ||
1162 kFieldChanged(before,
after, sfCloseInterestRate) ||
1163 kFieldChanged(before,
after, sfOverpaymentInterestRate) ||
1164 kFieldChanged(before,
after, sfStartDate) ||
1165 kFieldChanged(before,
after, sfPaymentInterval) ||
1166 kFieldChanged(before,
after, sfGracePeriod) ||
1167 kFieldChanged(before,
after, sfLoanScale);
1178 bad = bad || kFieldChanged(before,
after, sfVaultKind) ||
1179 kFieldChanged(before,
after, sfSubscriptionDate) ||
1180 kFieldChanged(before,
after, sfRedemptionDate) ||
1181 kFieldChanged(before,
after, sfSequence) ||
1182 kFieldChanged(before,
after, sfOwnerNode) ||
1183 kFieldChanged(before,
after, sfOwner) ||
1184 kFieldChanged(before,
after, sfWithdrawalPolicy) ||
1185 kFieldChanged(before,
after, sfScale) ||
1186 kFieldChanged(before,
after, sfLEVersion);
1194 "xrpl::NoModifiedUnmodifiableFields::finalize : no bad "
1195 "changes or enforce invariant");
1198 JLOG(j.
fatal()) <<
"Invariant failed: changed an unchangeable field for "
1213 if (!isDelete &&
after)
1231 <<
"Invariant failed: ledger entry contains non-canonical MPT or XRP amount";
1249 "xrpl::ObjectHasPseudoAccount::visitEntry : deleted ledger entry missing before state");
1254 switch (before->getType())
1280 bool failed =
false;
1283 if (!sle->isFieldPresent(sfAccount))
1286 <<
" is missing pseudo-account field";
1295 <<
" without deleting its pseudo-account";
A generic endpoint for log messages.
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
std::vector< std::pair< SLE::const_pointer, SLE::const_pointer > > accountsDeleted_
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
std::uint32_t accountsDeleted_
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
A currency issued by an account.
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool deepFreezeWithoutFreeze_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal 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, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
std::vector< SLE::const_pointer > deletedObjSles_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
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.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
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.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
constexpr TIss const & get() const
std::uint64_t mantissa() const noexcept
int signum() const noexcept
bool negative() const noexcept
bool native() const noexcept
Asset const & asset() const
AccountID const & getIssuer() const
LedgerEntryType getType() const
std::shared_ptr< STLedgerEntry const > const & const_ref
std::shared_ptr< STLedgerEntry const > const_pointer
AccountID getAccountID(SField const &field) const
STAmount const & getFieldAmount(SField const &field) const
TxType getTxnType() const
uint256 getTransactionID() const
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
static bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
std::vector< SLE::const_pointer > afterEntries_
std::uint32_t mptokensChanged_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
std::uint32_t trustlinesChanged_
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
std::uint32_t accountsCreated_
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
std::uint32_t accountSeq_
std::vector< std::string > errors_
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &)
constexpr value_type drops() const
Returns the number of drops.
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
void visitEntry(bool, SLE::const_ref, SLE::const_ref)
bool finalize(STTx const &, TER const, XRPAmount const, ReadView const &, beast::Journal const &) const
Keylet computation functions.
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Keylet nftokenPageMin(AccountID const &owner)
NFT page keylets.
Keylet nftokenPageMax(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
std::vector< SField const * > const & getPseudoAccountFields()
Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if set.
bool isFeatureEnabled(uint256 const &feature, bool resultIfNoRules)
Check whether a feature is enabled in the current ledger rules.
bool isXRP(AccountID const &c)
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
bool hasPrivilege(STTx const &tx, Privilege priv)
static std::optional< STAmount > clawbackTrustLineBalanceInHolderTerms(SLE::const_pointer const &sle, AccountID const &holder, AccountID const &issuer, Currency const ¤cy)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
static std::string ledgerEntryTypeName(SLE const &sle)
bool hasInvalidAmount(STBase const &field, beast::Journal j)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
bool isPseudoAccount(SLE::const_pointer sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
constexpr XRPAmount kInitialXrp
Configure the native currency.
std::array< KeyletDesc< AccountID const & >, 6 > const kDirectAccountKeylets
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
A pair of SHAMap key and LedgerEntryType.