1#include <xrpl/ledger/helpers/MPTokenHelpers.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/ledger/View.h>
5#include <xrpl/ledger/helpers/AccountRootHelpers.h>
6#include <xrpl/ledger/helpers/CredentialHelpers.h>
7#include <xrpl/ledger/helpers/DirectoryHelpers.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/Indexes.h>
10#include <xrpl/protocol/LedgerFormats.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/TxFlags.h>
20 return sle->isFlag(lsfMPTLocked);
28 return sle->isFlag(lsfMPTLocked);
49 for (
auto const& account : accounts)
55 for (
auto const& account : accounts)
71 sle && sle->isFieldPresent(sfTransferFee))
72 return Rate{1'000'000'000u + (10'000 * sle->getFieldU16(sfTransferFee))};
86 if (!issuance->isFlag(lsfMPTCanTransfer))
102 auto const& mptID = mptIssue.
getMptID();
106 if (mpt->isFlag(lsfMPTLocked))
120 MPTID const& mptIssuanceID,
138 if ((flags & tfMPTUnauthorize) != 0)
141 auto const sleMpt = view.
peek(mptokenKey);
142 if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
164 std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
166 (uOwnerCount < 2) ?
XRPAmount(beast::zero)
169 if (priorBalance < reserveCreate)
174 if (!mpt || mpt->getAccountID(sfIssuer) == account)
177 UNREACHABLE(
"xrpl::authorizeMPToken : invalid issuance or issuers token");
185 if (
auto ter =
dirLink(view, account, mptoken))
188 (*mptoken)[sfAccount] = account;
189 (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
190 (*mptoken)[sfFlags] = 0;
206 if (account != (*sleMptIssuance)[sfIssuer])
218 if ((flags & tfMPTUnauthorize) != 0)
220 flagsOut &= ~lsfMPTAuthorized;
226 flagsOut |= lsfMPTAuthorized;
229 if (flagsIn != flagsOut)
230 sleMpt->setFieldU32(sfFlags, flagsOut);
246 bool const accountIsIssuer = accountID == mptIssue.
getIssuer();
247 auto const& mptID = mptIssue.
getMptID();
255 if (mptoken->at(sfMPTAmount) != 0)
277 auto const sleIssuance = view.
read(mptID);
281 auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
284 if (mptIssuer == account)
287 bool const featureSAVEnabled = view.
rules().
enabled(featureSingleAssetVault);
289 if (featureSAVEnabled)
299 if (sleIssuer->isFieldPresent(sfVaultID))
301 auto const sleVault = view.
read(
keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
305 auto const asset = sleVault->at(sfAsset);
310 return requireAuth(view, issue, account, authType);
314 return requireAuth(view, issue, account, authType, depth + 1);
324 auto const sleToken = view.
read(mptokenID);
332 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
336 sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth,
337 "xrpl::requireAuth : issuance requires authorization");
352 if (featureSAVEnabled)
360 if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
361 (!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
370 MPTID const& mptIssuanceID,
380 sleIssuance->isFlag(lsfMPTRequireAuth),
381 "xrpl::enforceMPTokenAuthorization : authorization required");
383 if (account == sleIssuance->at(sfIssuer))
387 auto const sleToken = view.
read(keylet);
388 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
390 bool const authorizedByDomain = [&]() ->
bool {
392 if (!maybeDomainID.has_value())
403 if (!authorizedByDomain && sleToken ==
nullptr)
414 if (!authorizedByDomain && maybeDomainID.has_value())
421 if (!authorizedByDomain)
426 sleToken !=
nullptr && !maybeDomainID.has_value(),
427 "xrpl::enforceMPTokenAuthorization : found MPToken");
428 if (sleToken->isFlag(lsfMPTAuthorized))
433 if (authorizedByDomain && sleToken !=
nullptr)
438 maybeDomainID.has_value(),
439 "xrpl::enforceMPTokenAuthorization : found MPToken for domain");
442 if (authorizedByDomain)
447 maybeDomainID.has_value() && sleToken ==
nullptr,
448 "xrpl::enforceMPTokenAuthorization : new MPToken for domain");
462 UNREACHABLE(
"xrpl::enforceMPTokenAuthorization : condition list is incomplete");
475 auto const sleIssuance = view.
read(mptID);
479 if (!sleIssuance->isFlag(lsfMPTCanTransfer))
481 if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
494 auto const mptIssue = amount.get<
MPTIssue>();
496 auto sleIssuance = view.
peek(mptID);
499 JLOG(j.
error()) <<
"rippleLockEscrowMPT: MPT issuance not found for "
500 << mptIssue.getMptID();
504 if (amount.getIssuer() == sender)
506 JLOG(j.
error()) <<
"rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs.";
514 auto sle = view.
peek(mptokenID);
517 JLOG(j.
error()) <<
"rippleLockEscrowMPT: MPToken not found for " << sender;
521 auto const amt = sle->getFieldU64(sfMPTAmount);
522 auto const pay = amount.mpt().value();
527 JLOG(j.
error()) <<
"rippleLockEscrowMPT: insufficient MPTAmount for "
528 <<
to_string(sender) <<
": " << amt <<
" < " << pay;
532 (*sle)[sfMPTAmount] = amt - pay;
535 uint64_t
const locked = (*sle)[~sfLockedAmount].value_or(0);
539 JLOG(j.
error()) <<
"rippleLockEscrowMPT: overflow on locked amount for "
540 <<
to_string(sender) <<
": " << locked <<
" + " << pay;
544 if (sle->isFieldPresent(sfLockedAmount))
546 (*sle)[sfLockedAmount] += pay;
550 sle->setFieldU64(sfLockedAmount, pay);
559 uint64_t
const issuanceEscrowed = (*sleIssuance)[~sfLockedAmount].value_or(0);
560 auto const pay = amount.mpt().value();
565 JLOG(j.
error()) <<
"rippleLockEscrowMPT: overflow on issuance "
567 << mptIssue.getMptID() <<
": " << issuanceEscrowed <<
" + " << pay;
571 if (sleIssuance->isFieldPresent(sfLockedAmount))
573 (*sleIssuance)[sfLockedAmount] += pay;
577 sleIssuance->setFieldU64(sfLockedAmount, pay);
597 netAmount == grossAmount,
"xrpl::rippleUnlockEscrowMPT : netAmount == grossAmount");
600 auto const& issuer = netAmount.
getIssuer();
603 auto sleIssuance = view.
peek(mptID);
606 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: MPT issuance not found for "
607 << mptIssue.getMptID();
613 if (!sleIssuance->isFieldPresent(sfLockedAmount))
615 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: no locked amount in issuance for "
616 << mptIssue.getMptID();
620 auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
621 auto const redeem = grossAmount.
mpt().
value();
626 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient locked amount for "
627 << mptIssue.getMptID() <<
": " << locked <<
" < " << redeem;
631 auto const newLocked = locked - redeem;
634 sleIssuance->makeFieldAbsent(sfLockedAmount);
638 sleIssuance->setFieldU64(sfLockedAmount, newLocked);
643 if (issuer != receiver)
647 auto sle = view.
peek(mptokenID);
650 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: MPToken not found for " << receiver;
654 auto current = sle->getFieldU64(sfMPTAmount);
655 auto delta = netAmount.
mpt().
value();
660 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: overflow on MPTAmount for "
665 (*sle)[sfMPTAmount] += delta;
671 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
672 auto const redeem = netAmount.
mpt().
value();
677 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient outstanding amount for "
678 << mptIssue.getMptID() <<
": " << outstanding <<
" < " << redeem;
682 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
686 if (issuer == sender)
688 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: sender is the issuer, "
689 "cannot unlock MPTs.";
694 auto sle = view.
peek(mptokenID);
697 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: MPToken not found for " << sender;
701 if (!sle->isFieldPresent(sfLockedAmount))
703 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: no locked amount in MPToken for "
708 auto const locked = sle->getFieldU64(sfLockedAmount);
709 auto const delta = grossAmount.
mpt().
value();
714 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient locked amount for "
715 <<
to_string(sender) <<
": " << locked <<
" < " << delta;
719 auto const newLocked = locked - delta;
722 sle->makeFieldAbsent(sfLockedAmount);
726 sle->setFieldU64(sfLockedAmount, newLocked);
737 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
741 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient outstanding amount for "
742 << mptIssue.getMptID() <<
": " << outstanding <<
" < " << diff;
746 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
A generic endpoint for log messages.
Writeable view to a ledger, for applying a transaction.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
constexpr value_type value() const
Returns the underlying value.
constexpr MPTID const & getMptID() const
AccountID const & getIssuer() const
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
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.
constexpr TIss const & get() const
AccountID const & getIssuer() const
TER validDomain(ReadView const &view, uint256 domainID, AccountID const &subject)
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 mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::uint8_t constexpr maxAssetCheckDepth
Maximum recursion depth for vault shares being put as an asset inside another vault; counted from 0.
bool isIndividualFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue)
TER verifyValidDomain(ApplyView &view, AccountID const &account, uint256 domainID, beast::Journal j)
bool isVaultPseudoAccountFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptShare, int depth)
std::string to_string(base_uint< Bits, Tag > const &a)
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, MPTIssue const &mptIssue, beast::Journal journal)
@ expired
List is expired, but has the largest non-pending sequence seen so far.
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
TER authorizeMPToken(ApplyView &view, XRPAmount const &priorBalance, MPTID const &mptIssuanceID, AccountID const &account, beast::Journal journal, std::uint32_t flags=0, std::optional< AccountID > holderID=std::nullopt)
@ current
This was a new validation and was added.
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Check if the issuer has the global freeze flag set.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, int depth=0)
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
TERSubset< CanCvtToTER > TER
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
TER canAddHolding(ReadView const &view, MPTIssue const &mptIssue)
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
bool isTesSuccess(TER x) noexcept
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
@ tecINSUFFICIENT_RESERVE
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, int depth=0)
Check if the account lacks required authorization for MPT.
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Represents a transfer rate.