1#include <xrpl/basics/Expected.h>
2#include <xrpl/basics/Log.h>
3#include <xrpl/basics/chrono.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/ledger/CredentialHelpers.h>
6#include <xrpl/ledger/Credit.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/View.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/LedgerFormats.h>
12#include <xrpl/protocol/MPTIssue.h>
13#include <xrpl/protocol/Protocol.h>
14#include <xrpl/protocol/Quality.h>
15#include <xrpl/protocol/TER.h>
16#include <xrpl/protocol/TxFlags.h>
17#include <xrpl/protocol/digest.h>
18#include <xrpl/protocol/st.h>
40 auto const& svIndexes = page->getFieldV256(sfIndexes);
41 XRPL_ASSERT(index <= svIndexes.size(),
"xrpl::detail::internalDirNext : index inside range");
43 if (index >= svIndexes.size())
45 auto const next = page->getFieldU64(sfIndexNext);
58 XRPL_ASSERT(page,
"xrpl::detail::internalDirNext : non-null root");
68 entry = svIndexes[index++];
199 if (issuer != account)
231 if (issuer != account)
258 for (
auto const& account : accounts)
264 for (
auto const& account : accounts)
287 if (mptIssuance ==
nullptr)
290 auto const issuer = mptIssuance->getAccountID(sfIssuer);
292 if (mptIssuer ==
nullptr)
295 UNREACHABLE(
"xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
300 if (!mptIssuer->isFieldPresent(sfVaultID))
304 if (vault ==
nullptr)
306 UNREACHABLE(
"xrpl::isVaultPseudoAccountFrozen : null vault");
311 return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
326 if (issuer == account)
369 if (
isFrozen(view, account, currency, issuer) ||
384 else if (sleIssuer->isFieldPresent(sfAMMID))
392 (*sleAmm)[sfAsset].get<Issue>(),
393 (*sleAmm)[sfAsset2].get<Issue>()))
411 bool includeOppositeLimit,
417 amount = sle->getFieldAmount(sfBalance);
418 bool const accountHigh = account > issuer;
419 auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit;
425 if (includeOppositeLimit)
427 amount += sle->getFieldAmount(oppositeField);
429 amount.setIssuer(issuer);
433 amount.clear(
Issue{currency, issuer});
436 JLOG(j.
trace()) <<
"getTrustLineBalance:" <<
" account=" <<
to_string(account)
437 <<
" amount=" << amount.getFullText();
458 bool const returnSpendable = (includeFullBalance ==
shFULL_BALANCE);
459 if (returnSpendable && account == issuer)
481 view, account, issue.
currency, issue.
account, zeroIfFrozen, j, includeFullBalance);
494 bool const returnSpendable = (includeFullBalance ==
shFULL_BALANCE);
496 if (returnSpendable && account == mptIssue.
getIssuer())
509 issuance->at(sfOutstandingAmount)};
517 amount.clear(mptIssue);
519 amount.clear(mptIssue);
522 amount =
STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)};
531 amount.clear(mptIssue);
541 amount.clear(mptIssue);
548[[nodiscard]] STAmount
562 return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance);
567 view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
609 JLOG(j.fatal()) <<
"Account " << *
id <<
" owner count exceeds max!";
621 JLOG(j.fatal()) <<
"Account " << *
id <<
" owner count set below 0!";
624 XRPL_ASSERT(!
id,
"xrpl::confineOwnerCount : id is not set");
645 auto const fullBalance = sle->getFieldAmount(sfBalance);
649 STAmount const amount = (balance < reserve) ?
STAmount{0} : balance - reserve;
651 JLOG(j.
trace()) <<
"accountHolds:" <<
" account=" <<
to_string(
id)
652 <<
" amount=" << amount.getFullText()
653 <<
" fullBalance=" << fullBalance.getFullText()
654 <<
" balance=" << balance.getFullText() <<
" reserve=" << reserve
655 <<
" ownerCount=" << ownerCount <<
" ownerCountAdj=" << ownerCountAdj;
666 XRPL_ASSERT(
root.type == ltDIR_NODE,
"xrpl::forEachItem : valid root type");
668 if (
root.type != ltDIR_NODE)
675 auto sle = view.
read(pos);
678 for (
auto const& key : sle->getFieldV256(sfIndexes))
680 auto const next = sle->getFieldU64(sfIndexNext);
696 XRPL_ASSERT(
root.type == ltDIR_NODE,
"xrpl::forEachItemAfter : valid root type");
698 if (
root.type != ltDIR_NODE)
701 auto currentIndex =
root;
704 if (
after.isNonZero())
708 if (
auto hintDir = view.
read(hintIndex))
710 for (
auto const& key : hintDir->getFieldV256(sfIndexes))
715 currentIndex = hintIndex;
724 auto const ownerDir = view.
read(currentIndex);
727 for (
auto const& key : ownerDir->getFieldV256(sfIndexes))
740 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
750 auto const ownerDir = view.
read(currentIndex);
753 for (
auto const& key : ownerDir->getFieldV256(sfIndexes))
756 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
769 if (sle && sle->isFieldPresent(sfTransferRate))
770 return Rate{sle->getFieldU32(sfTransferRate)};
782 sle && sle->isFieldPresent(sfTransferFee))
783 return Rate{1'000'000'000u + 10'000 * sle->getFieldU16(sfTransferFee)};
798 amount.asset().value());
815 if (hash && (*hash != validLedger.
header().
hash))
817 JLOG(s) << reason <<
" incompatible with valid ledger";
819 JLOG(s) <<
"Hash(VSeq): " <<
to_string(*hash);
829 if (hash && (*hash != testLedger.
header().
hash))
831 JLOG(s) << reason <<
" incompatible preceding ledger";
833 JLOG(s) <<
"Hash(NSeq): " <<
to_string(*hash);
843 JLOG(s) << reason <<
" incompatible ledger";
850 JLOG(s) <<
"Val: " << validLedger.
header().
seq <<
" "
869 if (testLedger.
header().
seq > validIndex)
874 if (hash && (*hash != validHash))
876 JLOG(s) << reason <<
" incompatible following ledger";
877 JLOG(s) <<
"Hash(VSeq): " <<
to_string(*hash);
882 else if ((validIndex == testLedger.
header().
seq) && (testLedger.
header().
hash != validHash))
884 JLOG(s) << reason <<
" incompatible ledger";
891 JLOG(s) <<
"Val: " << validIndex <<
" " <<
to_string(validHash);
902 auto const sleNode = view.
read(k);
905 if (!sleNode->getFieldV256(sfIndexes).empty())
910 return sleNode->getFieldU64(sfIndexNext) == 0;
920 if (sle->isFieldPresent(sfAmendments))
922 auto const& v = sle->getFieldV256(sfAmendments);
923 amendments.insert(v.begin(), v.end());
937 if (sle->isFieldPresent(sfMajorities))
940 using d = tp::duration;
942 auto const majorities = sle->getFieldArray(sfMajorities);
944 for (
auto const& m : majorities)
945 ret[m.getFieldH256(sfAmendment)] = tp(d(m.getFieldU32(sfCloseTime)));
956 if (seq > ledger.
seq())
958 JLOG(journal.
warn()) <<
"Can't get seq " << seq <<
" from " << ledger.
seq() <<
" future";
961 if (seq == ledger.
seq())
963 if (seq == (ledger.
seq() - 1))
966 if (
int diff = ledger.
seq() - seq; diff <= 256)
973 hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.
seq() - 1),
974 "xrpl::hashOfSeq : matching ledger sequence");
975 STVector256 vec = hashIndex->getFieldV256(sfHashes);
976 if (vec.
size() >= diff)
977 return vec[vec.
size() - diff];
978 JLOG(journal.
warn()) <<
"Ledger " << ledger.
seq() <<
" missing hash for " << seq <<
" ("
979 << vec.
size() <<
"," << diff <<
")";
984 <<
" missing normal list";
988 if ((seq & 0xff) != 0)
990 JLOG(journal.
debug()) <<
"Can't get seq " << seq <<
" from " << ledger.
seq() <<
" past";
998 auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
999 XRPL_ASSERT(lastSeq >= seq,
"xrpl::hashOfSeq : minimum last ledger");
1000 XRPL_ASSERT((lastSeq & 0xff) == 0,
"xrpl::hashOfSeq : valid last ledger");
1001 auto const diff = (lastSeq - seq) >> 8;
1002 STVector256 vec = hashIndex->getFieldV256(sfHashes);
1003 if (vec.
size() > diff)
1004 return vec[vec.
size() - diff - 1];
1006 JLOG(journal.
warn()) <<
"Can't get seq " << seq <<
" from " << ledger.
seq() <<
" error";
1025 XRPL_ASSERT(amount,
"xrpl::adjustOwnerCount : nonzero amount input");
1030 sle->at(sfOwnerCount) = adjusted;
1051 object->setFieldU64(node, *page);
1064 rsh(hash.data(), hash.size());
1087 "xrpl::getPseudoAccountFields : unable to find account root "
1091 auto const& soTemplate = ar->getSOTemplate();
1094 for (
auto const& field : soTemplate)
1099 return pseudoFields;
1101 return pseudoFields;
1113 return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
1115 fields.begin(), fields.end(), [&sleAcct, &pseudoFieldFilter](
SField const* sf) ->
bool {
1116 return sleAcct->isFieldPresent(*sf) &&
1117 (pseudoFieldFilter.empty() || pseudoFieldFilter.contains(sf));
1121Expected<std::shared_ptr<SLE>,
TER>
1130 [&ownerField](
SField const* sf) ->
bool { return *sf == ownerField; }) == 1,
1131 "xrpl::createPseudoAccount : valid owner field");
1134 if (accountId == beast::zero)
1139 account->setAccountID(sfAccount, accountId);
1140 account->setFieldAmount(sfBalance,
STAmount{});
1150 account->setFieldU32(sfSequence, seqno);
1156 account->setFieldH256(ownerField, pseudoOwnerKey);
1202 if (toSle ==
nullptr)
1236 auto const& issuer = amount.getIssuer();
1237 if (from == to || to == issuer ||
isXRP(issuer))
1244 auto const& currency = issue.currency;
1245 auto const owed =
creditBalance(view, to, issuer, currency);
1246 if (owed <= beast::zero)
1248 auto const limit =
creditLimit(view, to, issuer, currency);
1249 if (-owed >= limit || amount > (limit + owed))
1255 amount.asset().value());
1265 bool hasDestinationTag)
1288 bool hasDestinationTag)
1292 return canWithdraw(view, from, to, toSle, amount, hasDestinationTag);
1298 auto const from = tx[sfAccount];
1299 auto const to = tx[~sfDestination].value_or(from);
1316 if (dstAcct == senderAcct)
1318 if (
auto const ter =
addEmptyHolding(view, senderAcct, priorBalance, amount.asset(), j);
1339 JLOG(j.
error()) <<
"LoanBrokerCoverWithdraw: negative balance of "
1340 "broker cover assets.";
1362 auto const& issuerId = issue.
getIssuer();
1363 auto const& currency = issue.
currency;
1367 auto const& srcId = issuerId;
1368 auto const& dstId = accountID;
1369 auto const high = srcId > dstId;
1370 auto const index =
keylet::line(srcId, dstId, currency);
1373 if (!sleDst || !sleSrc)
1378 if (view.
read(index))
1397 STAmount{Issue{currency, noAccount()}},
1398 STAmount{Issue{currency, dstId}},
1412 auto const& mptID = mptIssue.
getMptID();
1413 auto const mpt = view.
peek(keylet::mptIssuance(mptID));
1418 if (view.
peek(keylet::mptoken(mptID, accountID)))
1430 MPTID const& mptIssuanceID,
1436 auto const sleAcct = view.
peek(keylet::account(account));
1450 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
1451 auto const sleMpt = view.
peek(mptokenKey);
1452 if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
1456 keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(),
false))
1474 std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
1476 (uOwnerCount < 2) ?
XRPAmount(beast::zero)
1479 if (priorBalance < reserveCreate)
1483 auto const mpt = view.
read(keylet::mptIssuance(mptIssuanceID));
1484 if (!mpt || mpt->getAccountID(sfIssuer) == account)
1487 UNREACHABLE(
"xrpl::authorizeMPToken : invalid issuance or issuers token");
1493 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
1495 if (
auto ter =
dirLink(view, account, mptoken))
1498 (*mptoken)[sfAccount] = account;
1499 (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
1500 (*mptoken)[sfFlags] = 0;
1509 auto const sleMptIssuance = view.
read(keylet::mptIssuance(mptIssuanceID));
1510 if (!sleMptIssuance)
1516 if (account != (*sleMptIssuance)[sfIssuer])
1519 auto const sleMpt = view.
peek(keylet::mptoken(mptIssuanceID, *holderID));
1529 flagsOut &= ~lsfMPTAuthorized;
1535 if (flagsIn != flagsOut)
1536 sleMpt->setFieldU32(sfFlags, flagsOut);
1545 bool const bSrcHigh,
1551 bool const bNoRipple,
1562 JLOG(j.
trace()) <<
"trustCreate: " << to_string(uSrcAccountID) <<
", "
1563 << to_string(uDstAccountID) <<
", " << saBalance.
getFullText();
1565 auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
1566 auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
1567 if (uLowAccountID == uHighAccountID)
1570 UNREACHABLE(
"xrpl::trustCreate : trust line to self");
1577 view.
insert(sleRippleState);
1580 keylet::ownerDir(uLowAccountID), sleRippleState->key(),
describeOwnerDir(uLowAccountID));
1586 keylet::ownerDir(uHighAccountID), sleRippleState->key(),
describeOwnerDir(uHighAccountID));
1591 bool const bSetDst = saLimit.
getIssuer() == uDstAccountID;
1592 bool const bSetHigh = bSrcHigh ^ bSetDst;
1594 XRPL_ASSERT(sleAccount,
"xrpl::trustCreate : non-null SLE");
1599 sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
1600 "xrpl::trustCreate : matching account ID");
1601 auto const slePeer = view.
peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
1606 sleRippleState->setFieldU64(sfLowNode, *lowNode);
1607 sleRippleState->setFieldU64(sfHighNode, *highNode);
1609 sleRippleState->setFieldAmount(bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
1610 sleRippleState->setFieldAmount(
1611 bSetHigh ? sfLowLimit : sfHighLimit,
1615 sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
1618 sleRippleState->setFieldU32(bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
1645 sleRippleState->setFieldU32(sfFlags, uFlags);
1649 sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance);
1651 view.
creditHook(uSrcAccountID, uDstAccountID, saBalance, saBalance.
zeroed());
1665 auto const sle = view.
read(keylet::account(accountID));
1669 auto const balance = sle->getFieldAmount(sfBalance);
1670 if (balance.xrp() != 0)
1679 bool const accountIsIssuer = accountID == issue.
account;
1680 auto const line = view.
peek(keylet::line(accountID, issue));
1683 if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::zero)
1690 auto sleLowAccount = view.
peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
1704 auto sleHighAccount = view.
peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
1705 if (!sleHighAccount)
1716 view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal);
1729 bool const accountIsIssuer = accountID == mptIssue.
getIssuer();
1730 auto const& mptID = mptIssue.
getMptID();
1731 auto const mptoken = view.
peek(keylet::mptoken(mptID, accountID));
1738 if (mptoken->at(sfMPTAmount) != 0)
1760 std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode);
1761 std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode);
1763 JLOG(j.
trace()) <<
"trustDelete: Deleting ripple line: low";
1765 if (!view.
dirRemove(keylet::ownerDir(uLowAccountID), uLowNode, sleRippleState->key(),
false))
1770 JLOG(j.
trace()) <<
"trustDelete: Deleting ripple line: high";
1772 if (!view.
dirRemove(keylet::ownerDir(uHighAccountID), uHighNode, sleRippleState->key(),
false))
1777 JLOG(j.
trace()) <<
"trustDelete: Deleting ripple line: state";
1778 view.
erase(sleRippleState);
1788 auto offerIndex = sle->key();
1789 auto owner = sle->getAccountID(sfAccount);
1792 uint256 uDirectory = sle->getFieldH256(sfBookDirectory);
1794 if (!view.
dirRemove(keylet::ownerDir(owner), sle->getFieldU64(sfOwnerNode), offerIndex,
false))
1799 if (!view.
dirRemove(keylet::page(uDirectory), sle->getFieldU64(sfBookNode), offerIndex,
false))
1804 if (sle->isFieldPresent(sfAdditionalBooks))
1807 sle->isFlag(
lsfHybrid) && sle->isFieldPresent(sfDomainID),
1808 "xrpl::offerDelete : should be a hybrid domain offer");
1810 auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks);
1812 for (
auto const& bookDir : additionalBookDirs)
1814 auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory);
1815 auto const& dirNode = bookDir.getFieldU64(sfBookNode);
1817 if (!view.
dirRemove(keylet::page(dirIndex), dirNode, offerIndex,
false))
1849 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
1850 "xrpl::rippleCreditIOU : matching issuer or don't care");
1854 XRPL_ASSERT(uSenderID != uReceiverID,
"xrpl::rippleCreditIOU : sender is not receiver");
1856 bool const bSenderHigh = uSenderID > uReceiverID;
1857 auto const index = keylet::line(uSenderID, uReceiverID, currency);
1860 !
isXRP(uSenderID) && uSenderID !=
noAccount(),
"xrpl::rippleCreditIOU : sender is not XRP");
1863 "xrpl::rippleCreditIOU : receiver is not XRP");
1866 if (
auto const sleRippleState = view.
peek(index))
1868 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
1873 view.
creditHook(uSenderID, uReceiverID, saAmount, saBalance);
1875 STAmount const saBefore = saBalance;
1877 saBalance -= saAmount;
1879 JLOG(j.
trace()) <<
"rippleCreditIOU: " << to_string(uSenderID) <<
" -> "
1880 << to_string(uReceiverID) <<
" : before=" << saBefore.
getFullText()
1884 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
1885 bool bDelete =
false;
1889 if (saBefore > beast::zero
1891 && saBalance <= beast::zero
1899 !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
1901 && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
1903 && !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
1910 sleRippleState->setFieldU32(
1911 sfFlags, uFlags & (!bSenderHigh ? ~
lsfLowReserve : ~lsfHighReserve));
1914 bDelete = !saBalance
1923 sleRippleState->setFieldAmount(sfBalance, saBalance);
1931 bSenderHigh ? uReceiverID : uSenderID,
1932 !bSenderHigh ? uReceiverID : uSenderID,
1936 view.
update(sleRippleState);
1940 STAmount const saReceiverLimit(
Issue{currency, uReceiverID});
1945 JLOG(j.
debug()) <<
"rippleCreditIOU: "
1947 << to_string(uSenderID) <<
" -> " << to_string(uReceiverID) <<
" : "
1950 auto const sleAccount = view.
peek(keylet::account(uReceiverID));
1987 auto const& issuer = saAmount.
getIssuer();
1991 "xrpl::rippleSendIOU : neither sender nor receiver is XRP");
1992 XRPL_ASSERT(uSenderID != uReceiverID,
"xrpl::rippleSendIOU : sender is not receiver");
1994 if (uSenderID == issuer || uReceiverID == issuer || issuer ==
noAccount())
1997 auto const ter =
rippleCreditIOU(view, uSenderID, uReceiverID, saAmount,
false, j);
2000 saActual = saAmount;
2008 saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount
2011 JLOG(j.
debug()) <<
"rippleSendIOU> " << to_string(uSenderID) <<
" - > "
2012 << to_string(uReceiverID) <<
" : deliver=" << saAmount.
getFullText()
2018 terResult =
rippleCreditIOU(view, uSenderID, issuer, saActual,
true, j);
2038 XRPL_ASSERT(!
isXRP(senderID),
"xrpl::rippleSendMultiIOU : sender is not XRP");
2042 actual = takeFromSender;
2045 for (
auto const& r : receivers)
2047 auto const& receiverID = r.first;
2053 if (!amount || (senderID == receiverID))
2056 XRPL_ASSERT(!
isXRP(receiverID),
"xrpl::rippleSendMultiIOU : receiver is not XRP");
2058 if (senderID == issuer || receiverID == issuer || issuer ==
noAccount())
2061 if (
auto const ter =
rippleCreditIOU(view, senderID, receiverID, amount,
false, j))
2074 STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
2077 actual += actualSend;
2078 takeFromSender += actualSend;
2080 JLOG(j.
debug()) <<
"rippleSendMultiIOU> " << to_string(senderID) <<
" - > "
2081 << to_string(receiverID) <<
" : deliver=" << amount.getFullText()
2084 if (
TER const terResult =
rippleCreditIOU(view, issuer, receiverID, amount,
true, j))
2088 if (senderID != issuer && takeFromSender)
2090 if (
TER const terResult =
rippleCreditIOU(view, senderID, issuer, takeFromSender,
true, j))
2118 "xrpl::accountSendIOU : minimum amount and not MPT");
2125 if (!saAmount || (uSenderID == uReceiverID))
2132 JLOG(j.
trace()) <<
"accountSendIOU: " << to_string(uSenderID) <<
" -> "
2133 << to_string(uReceiverID) <<
" : " << saAmount.
getFullText();
2135 return rippleSendIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
2146 uSenderID != beast::zero ? view.
peek(keylet::account(uSenderID)) :
SLE::pointer();
2148 uReceiverID != beast::zero ? view.
peek(keylet::account(uReceiverID)) :
SLE::pointer();
2150 if (
auto stream = j.
trace())
2156 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
2159 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
2161 stream <<
"accountSendIOU> " << to_string(uSenderID) <<
" (" << sender_bal <<
") -> "
2162 << to_string(uReceiverID) <<
" (" << receiver_bal
2168 if (sender->getFieldAmount(sfBalance) < saAmount)
2178 auto const sndBal = sender->getFieldAmount(sfBalance);
2182 sender->setFieldAmount(sfBalance, sndBal - saAmount);
2190 auto const rcvBal = receiver->getFieldAmount(sfBalance);
2191 receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
2197 if (
auto stream = j.
trace())
2203 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
2206 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
2208 stream <<
"accountSendIOU< " << to_string(uSenderID) <<
" (" << sender_bal <<
") -> "
2209 << to_string(uReceiverID) <<
" (" << receiver_bal
2226 receivers.
size() > 1,
"xrpl::accountSendMultiIOU",
"multiple recipients provided");
2231 JLOG(j.
trace()) <<
"accountSendMultiIOU: " << to_string(senderID) <<
" sending "
2232 << receivers.
size() <<
" IOUs";
2244 senderID != beast::zero ? view.
peek(keylet::account(senderID)) :
SLE::pointer();
2246 if (
auto stream = j.
trace())
2251 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
2253 stream <<
"accountSendMultiIOU> " << to_string(senderID) <<
" (" << sender_bal <<
") -> "
2254 << receivers.
size() <<
" receivers.";
2259 for (
auto const& r : receivers)
2261 auto const& receiverID = r.first;
2264 if (amount < beast::zero)
2272 if (!amount || (senderID == receiverID))
2276 receiverID != beast::zero ? view.
peek(keylet::account(receiverID)) :
SLE::pointer();
2278 if (
auto stream = j.
trace())
2283 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
2285 stream <<
"accountSendMultiIOU> " << to_string(senderID) <<
" -> "
2286 << to_string(receiverID) <<
" (" << receiver_bal
2287 <<
") : " << amount.getFullText();
2293 auto const rcvBal = receiver->getFieldAmount(sfBalance);
2294 receiver->setFieldAmount(sfBalance, rcvBal + amount);
2300 takeFromSender += amount;
2303 if (
auto stream = j.
trace())
2308 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
2310 stream <<
"accountSendMultiIOU< " << to_string(senderID) <<
" -> "
2311 << to_string(receiverID) <<
" (" << receiver_bal
2312 <<
") : " << amount.getFullText();
2318 if (sender->getFieldAmount(sfBalance) < takeFromSender)
2324 auto const sndBal = sender->getFieldAmount(sfBalance);
2328 sender->setFieldAmount(sfBalance, sndBal - takeFromSender);
2333 if (
auto stream = j.
trace())
2339 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
2341 stream <<
"accountSendMultiIOU< " << to_string(senderID) <<
" (" << sender_bal <<
") -> "
2342 << receivers.
size() <<
" receivers.";
2357 auto const& issuer = saAmount.
getIssuer();
2358 auto sleIssuance = view.
peek(mptID);
2361 if (uSenderID == issuer)
2363 (*sleIssuance)[sfOutstandingAmount] += saAmount.
mpt().
value();
2364 view.
update(sleIssuance);
2368 auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
2369 if (
auto sle = view.
peek(mptokenID))
2371 auto const amt = sle->getFieldU64(sfMPTAmount);
2372 auto const pay = saAmount.
mpt().
value();
2375 (*sle)[sfMPTAmount] = amt - pay;
2382 if (uReceiverID == issuer)
2384 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
2385 auto const redeem = saAmount.
mpt().
value();
2386 if (outstanding >= redeem)
2388 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
2389 view.
update(sleIssuance);
2396 auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
2397 if (
auto sle = view.
peek(mptokenID))
2399 (*sle)[sfMPTAmount] += saAmount.
mpt().
value();
2419 XRPL_ASSERT(uSenderID != uReceiverID,
"xrpl::rippleSendMPT : sender is not receiver");
2422 auto const& issuer = saAmount.
getIssuer();
2428 if (uSenderID == issuer || uReceiverID == issuer)
2432 if (uSenderID == issuer)
2434 auto const sendAmount = saAmount.
mpt().
value();
2435 auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(
maxMPTokenAmount);
2436 if (sendAmount > maximumAmount ||
2437 sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
2442 auto const ter =
rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
2445 saActual = saAmount;
2450 saActual = (waiveFee == WaiveTransferFee::Yes)
2454 JLOG(j.
debug()) <<
"rippleSendMPT> " << to_string(uSenderID) <<
" - > "
2455 << to_string(uReceiverID) <<
" : deliver=" << saAmount.
getFullText()
2458 if (
auto const terResult =
rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
2477 auto const& issuer = mptIssue.
getIssuer();
2479 auto const sle = view.
read(keylet::mptIssuance(mptIssue.
getMptID()));
2485 actual = takeFromSender;
2487 for (
auto const& r : receivers)
2489 auto const& receiverID = r.first;
2490 STAmount amount{mptIssue, r.second};
2492 if (amount < beast::zero)
2500 if (!amount || (senderID == receiverID))
2503 if (senderID == issuer || receiverID == issuer)
2507 if (senderID == issuer)
2510 takeFromSender == beast::zero,
2511 "rippler::rippleSendMultiMPT",
2512 "sender == issuer, takeFromSender == zero");
2513 auto const sendAmount = amount.mpt().value();
2514 auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(
maxMPTokenAmount);
2515 if (sendAmount > maximumAmount ||
2516 sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
2521 if (
auto const ter =
rippleCreditMPT(view, senderID, receiverID, amount, j))
2531 STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
2534 actual += actualSend;
2535 takeFromSender += actualSend;
2537 JLOG(j.
debug()) <<
"rippleSendMultiMPT> " << to_string(senderID) <<
" - > "
2538 << to_string(receiverID) <<
" : deliver=" << amount.getFullText()
2541 if (
auto const terResult =
rippleCreditMPT(view, issuer, receiverID, amount, j))
2544 if (senderID != issuer && takeFromSender)
2546 if (
TER const terResult =
rippleCreditMPT(view, senderID, issuer, takeFromSender, j))
2564 "xrpl::accountSendMPT : minimum amount and MPT");
2569 if (!saAmount || (uSenderID == uReceiverID))
2574 return rippleSendMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
2603 return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
2605 return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
2620 receivers.
size() > 1,
"xrpl::accountSendMulti",
"multiple recipients provided");
2645 auto sle = view.
peek(keylet::account(sender));
2650 if (before > beast::zero
2652 &&
after <= beast::zero
2659 !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
2661 && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
2663 && !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
2671 state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~
lsfLowReserve : ~lsfHighReserve));
2691 "xrpl::issueIOU : neither account nor issuer is XRP");
2694 XRPL_ASSERT(issue == amount.issue(),
"xrpl::issueIOU : matching issue");
2697 XRPL_ASSERT(issue.
account != account,
"xrpl::issueIOU : not issuer account");
2699 JLOG(j.
trace()) <<
"issueIOU: " << to_string(account) <<
": " << amount.getFullText();
2701 bool bSenderHigh = issue.
account > account;
2703 auto const index = keylet::line(issue.
account, account, issue.
currency);
2705 if (
auto state = view.
peek(index))
2707 STAmount final_balance = state->getFieldAmount(sfBalance);
2712 STAmount const start_balance = final_balance;
2714 final_balance -= amount;
2717 view, state, bSenderHigh, issue.
account, start_balance, final_balance, j);
2727 state->setFieldAmount(sfBalance, final_balance);
2732 bSenderHigh ? account : issue.
account,
2733 bSenderHigh ? issue.
account : account,
2749 auto const receiverAccount = view.
peek(keylet::account(account));
2750 if (!receiverAccount)
2783 "xrpl::redeemIOU : neither account nor issuer is XRP");
2786 XRPL_ASSERT(issue == amount.issue(),
"xrpl::redeemIOU : matching issue");
2789 XRPL_ASSERT(issue.
account != account,
"xrpl::redeemIOU : not issuer account");
2791 JLOG(j.
trace()) <<
"redeemIOU: " << to_string(account) <<
": " << amount.getFullText();
2793 bool bSenderHigh = account > issue.
account;
2797 STAmount final_balance = state->getFieldAmount(sfBalance);
2802 STAmount const start_balance = final_balance;
2804 final_balance -= amount;
2806 auto const must_delete =
2807 updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j);
2817 state->setFieldAmount(sfBalance, final_balance);
2824 bSenderHigh ? issue.
account : account,
2825 bSenderHigh ? account : issue.
account,
2837 JLOG(j.
fatal()) <<
"redeemIOU: " << to_string(account) <<
" attempts to redeem "
2838 << amount.getFullText() <<
" but no trust line exists!";
2852 XRPL_ASSERT(from != beast::zero,
"xrpl::transferXRP : nonzero from account");
2853 XRPL_ASSERT(to != beast::zero,
"xrpl::transferXRP : nonzero to account");
2854 XRPL_ASSERT(from != to,
"xrpl::transferXRP : sender is not receiver");
2855 XRPL_ASSERT(amount.native(),
"xrpl::transferXRP : amount is XRP");
2859 if (!sender || !receiver)
2862 JLOG(j.
trace()) <<
"transferXRP: " << to_string(from) <<
" -> " << to_string(to)
2863 <<
") : " << amount.getFullText();
2865 if (sender->getFieldAmount(sfBalance) < amount)
2876 sender->setFieldAmount(sfBalance, sender->getFieldAmount(sfBalance) - amount);
2879 receiver->setFieldAmount(sfBalance, receiver->getFieldAmount(sfBalance) + amount);
2893 if (!trustLine && authType == AuthType::StrongAuth)
2898 if (
auto const issuerAccount = view.
read(keylet::account(issue.
account));
2919 auto const mptID = keylet::mptIssuance(mptIssue.
getMptID());
2920 auto const sleIssuance = view.
read(mptID);
2924 auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
2927 if (mptIssuer == account)
2930 bool const featureSAVEnabled = view.
rules().
enabled(featureSingleAssetVault);
2932 if (featureSAVEnabled)
2938 auto const sleIssuer = view.
read(keylet::account(mptIssuer));
2942 if (sleIssuer->isFieldPresent(sfVaultID))
2944 auto const sleVault = view.
read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
2948 auto const asset = sleVault->at(sfAsset);
2952 return requireAuth(view, issue, account, authType);
2954 return requireAuth(view, issue, account, authType, depth + 1);
2962 auto const mptokenID = keylet::mptoken(mptID.key, account);
2963 auto const sleToken = view.
read(mptokenID);
2966 if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy))
2971 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
2976 "xrpl::requireAuth : issuance requires authorization");
2978 if (
auto const ter = credentials::validDomain(view, *maybeDomainID, account);
2987 if (featureSAVEnabled)
3005 MPTID const& mptIssuanceID,
3010 auto const sleIssuance = view.
read(keylet::mptIssuance(mptIssuanceID));
3016 "xrpl::enforceMPTokenAuthorization : authorization required");
3018 if (account == sleIssuance->at(sfIssuer))
3021 auto const keylet = keylet::mptoken(mptIssuanceID, account);
3022 auto const sleToken = view.
read(keylet);
3023 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
3025 bool const authorizedByDomain = [&]() ->
bool {
3027 if (!maybeDomainID.has_value())
3038 if (!authorizedByDomain && sleToken ==
nullptr)
3049 else if (!authorizedByDomain && maybeDomainID.has_value())
3056 else if (!authorizedByDomain)
3061 sleToken !=
nullptr && !maybeDomainID.has_value(),
3062 "xrpl::enforceMPTokenAuthorization : found MPToken");
3068 else if (authorizedByDomain && sleToken !=
nullptr)
3073 maybeDomainID.has_value(),
3074 "xrpl::enforceMPTokenAuthorization : found MPToken for domain");
3077 else if (authorizedByDomain)
3082 maybeDomainID.has_value() && sleToken ==
nullptr,
3083 "xrpl::enforceMPTokenAuthorization : new MPToken for domain");
3097 UNREACHABLE(
"xrpl::enforceMPTokenAuthorization : condition list is incomplete");
3109 auto const mptID = keylet::mptIssuance(mptIssue.
getMptID());
3110 auto const sleIssuance = view.
read(mptID);
3116 if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
3128 auto const& issuerId = issue.
getIssuer();
3129 if (issuerId == from || issuerId == to)
3131 auto const sleIssuer = view.
read(keylet::account(issuerId));
3132 if (sleIssuer ==
nullptr)
3135 auto const isRippleDisabled = [&](
AccountID account) ->
bool {
3138 auto const line = view.
read(keylet::line(account, issue));
3141 bool const issuerHigh = issuerId > account;
3148 if (isRippleDisabled(from) && isRippleDisabled(to))
3157 Keylet const& ownerDirKeylet,
3164 unsigned int uDirEntry{0};
3165 uint256 dirEntry{beast::zero};
3168 if (view.
exists(ownerDirKeylet) &&
3169 dirFirst(view, ownerDirKeylet.
key, sleDirNode, uDirEntry, dirEntry))
3173 if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
3177 auto sleItem = view.
peek(keylet::child(dirEntry));
3182 JLOG(j.
fatal()) <<
"DeleteAccount: Directory node in ledger " << view.
seq()
3183 <<
" has index to object that is missing: " << to_string(dirEntry);
3189 safe_cast<LedgerEntryType>(sleItem->getFieldU16(sfLedgerEntryType))};
3193 auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
3213 XRPL_ASSERT(uDirEntry >= 1,
"xrpl::cleanupOnAccountDelete : minimum dir entries");
3217 JLOG(j.
error()) <<
"DeleteAccount iterator re-validation failed.";
3221 if (skipEntry == SkipEntry::No)
3224 }
while (
dirNext(view, ownerDirKeylet.
key, sleDirNode, uDirEntry, dirEntry));
3237 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
3241 sleState->getFieldAmount(sfLowLimit).getIssuer(),
3242 sleState->getFieldAmount(sfHighLimit).getIssuer());
3243 auto sleLow = view.
peek(keylet::account(low));
3244 auto sleHigh = view.
peek(keylet::account(high));
3245 if (!sleLow || !sleHigh)
3248 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
3249 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
3252 if (ammLow && ammHigh)
3256 if (!ammLow && !ammHigh)
3260 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
3265 JLOG(j.
error()) <<
"deleteAMMTrustLine: failed to delete the trustline.";
3270 if (!(sleState->getFlags() & uFlags))
3291 return rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
3295 XRPL_ASSERT(!bCheckIssuer,
"xrpl::rippleCredit : not checking issuer");
3308 XRPL_ASSERT(!assets.
negative(),
"xrpl::assetsToSharesDeposit : non-negative assets");
3310 assets.
asset() == vault->at(sfAsset),
3311 "xrpl::assetsToSharesDeposit : assets and vault match");
3312 if (assets.
negative() || assets.
asset() != vault->at(sfAsset))
3315 Number const assetTotal = vault->at(sfAssetsTotal);
3316 STAmount shares{vault->at(sfShareMPTID)};
3317 if (assetTotal == 0)
3322 Number const shareTotal = issuance->at(sfOutstandingAmount);
3323 shares = ((shareTotal * assets) / assetTotal).truncate();
3333 XRPL_ASSERT(!shares.
negative(),
"xrpl::sharesToAssetsDeposit : non-negative shares");
3335 shares.
asset() == vault->at(sfShareMPTID),
3336 "xrpl::sharesToAssetsDeposit : shares and vault match");
3337 if (shares.
negative() || shares.
asset() != vault->at(sfShareMPTID))
3340 Number const assetTotal = vault->at(sfAssetsTotal);
3341 STAmount assets{vault->at(sfAsset)};
3342 if (assetTotal == 0)
3346 Number const shareTotal = issuance->at(sfOutstandingAmount);
3347 assets = (assetTotal * shares) / shareTotal;
3358 XRPL_ASSERT(!assets.
negative(),
"xrpl::assetsToSharesDeposit : non-negative assets");
3360 assets.
asset() == vault->at(sfAsset),
3361 "xrpl::assetsToSharesWithdraw : assets and vault match");
3362 if (assets.
negative() || assets.
asset() != vault->at(sfAsset))
3365 Number assetTotal = vault->at(sfAssetsTotal);
3366 assetTotal -= vault->at(sfLossUnrealized);
3367 STAmount shares{vault->at(sfShareMPTID)};
3368 if (assetTotal == 0)
3370 Number const shareTotal = issuance->at(sfOutstandingAmount);
3371 Number result = (shareTotal * assets) / assetTotal;
3372 if (truncate == TruncateShares::yes)
3384 XRPL_ASSERT(!shares.
negative(),
"xrpl::sharesToAssetsDeposit : non-negative shares");
3386 shares.
asset() == vault->at(sfShareMPTID),
3387 "xrpl::sharesToAssetsWithdraw : shares and vault match");
3388 if (shares.
negative() || shares.
asset() != vault->at(sfShareMPTID))
3391 Number assetTotal = vault->at(sfAssetsTotal);
3392 assetTotal -= vault->at(sfLossUnrealized);
3393 STAmount assets{vault->at(sfAsset)};
3394 if (assetTotal == 0)
3396 Number const shareTotal = issuance->at(sfOutstandingAmount);
3397 assets = (assetTotal * shares) / shareTotal;
3408 auto const mptIssue = amount.get<
MPTIssue>();
3409 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
3410 auto sleIssuance = view.
peek(mptID);
3413 JLOG(j.
error()) <<
"rippleLockEscrowMPT: MPT issuance not found for "
3414 << mptIssue.getMptID();
3418 if (amount.getIssuer() == sender)
3420 JLOG(j.
error()) <<
"rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs.";
3427 auto const mptokenID = keylet::mptoken(mptID.key, sender);
3428 auto sle = view.
peek(mptokenID);
3431 JLOG(j.
error()) <<
"rippleLockEscrowMPT: MPToken not found for " << sender;
3435 auto const amt = sle->getFieldU64(sfMPTAmount);
3436 auto const pay = amount.mpt().value();
3441 JLOG(j.
error()) <<
"rippleLockEscrowMPT: insufficient MPTAmount for "
3442 << to_string(sender) <<
": " << amt <<
" < " << pay;
3446 (*sle)[sfMPTAmount] = amt - pay;
3449 uint64_t
const locked = (*sle)[~sfLockedAmount].value_or(0);
3453 JLOG(j.
error()) <<
"rippleLockEscrowMPT: overflow on locked amount for "
3454 << to_string(sender) <<
": " << locked <<
" + " << pay;
3458 if (sle->isFieldPresent(sfLockedAmount))
3459 (*sle)[sfLockedAmount] += pay;
3461 sle->setFieldU64(sfLockedAmount, pay);
3469 uint64_t
const issuanceEscrowed = (*sleIssuance)[~sfLockedAmount].value_or(0);
3470 auto const pay = amount.mpt().value();
3475 JLOG(j.
error()) <<
"rippleLockEscrowMPT: overflow on issuance "
3476 "locked amount for "
3477 << mptIssue.getMptID() <<
": " << issuanceEscrowed <<
" + " << pay;
3481 if (sleIssuance->isFieldPresent(sfLockedAmount))
3482 (*sleIssuance)[sfLockedAmount] += pay;
3484 sleIssuance->setFieldU64(sfLockedAmount, pay);
3486 view.
update(sleIssuance);
3502 netAmount == grossAmount,
"xrpl::rippleUnlockEscrowMPT : netAmount == grossAmount");
3504 auto const& issuer = netAmount.
getIssuer();
3506 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
3507 auto sleIssuance = view.
peek(mptID);
3510 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: MPT issuance not found for "
3511 << mptIssue.getMptID();
3517 if (!sleIssuance->isFieldPresent(sfLockedAmount))
3519 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: no locked amount in issuance for "
3520 << mptIssue.getMptID();
3524 auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
3525 auto const redeem = grossAmount.
mpt().
value();
3530 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient locked amount for "
3531 << mptIssue.getMptID() <<
": " << locked <<
" < " << redeem;
3535 auto const newLocked = locked - redeem;
3537 sleIssuance->makeFieldAbsent(sfLockedAmount);
3539 sleIssuance->setFieldU64(sfLockedAmount, newLocked);
3540 view.
update(sleIssuance);
3543 if (issuer != receiver)
3546 auto const mptokenID = keylet::mptoken(mptID.key, receiver);
3547 auto sle = view.
peek(mptokenID);
3550 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: MPToken not found for " << receiver;
3554 auto current = sle->getFieldU64(sfMPTAmount);
3555 auto delta = netAmount.
mpt().
value();
3560 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: overflow on MPTAmount for "
3561 << to_string(receiver) <<
": " <<
current <<
" + " << delta;
3565 (*sle)[sfMPTAmount] += delta;
3571 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
3572 auto const redeem = netAmount.
mpt().
value();
3577 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient outstanding amount for "
3578 << mptIssue.getMptID() <<
": " << outstanding <<
" < " << redeem;
3582 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
3583 view.
update(sleIssuance);
3586 if (issuer == sender)
3588 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: sender is the issuer, "
3589 "cannot unlock MPTs.";
3595 auto const mptokenID = keylet::mptoken(mptID.key, sender);
3596 auto sle = view.
peek(mptokenID);
3599 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: MPToken not found for " << sender;
3603 if (!sle->isFieldPresent(sfLockedAmount))
3605 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: no locked amount in MPToken for "
3606 << to_string(sender);
3610 auto const locked = sle->getFieldU64(sfLockedAmount);
3611 auto const delta = grossAmount.
mpt().
value();
3616 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient locked amount for "
3617 << to_string(sender) <<
": " << locked <<
" < " << delta;
3621 auto const newLocked = locked - delta;
3623 sle->makeFieldAbsent(sfLockedAmount);
3625 sle->setFieldU64(sfLockedAmount, newLocked);
3636 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
3640 JLOG(j.
error()) <<
"rippleUnlockEscrowMPT: insufficient outstanding amount for "
3641 << mptIssue.getMptID() <<
": " << outstanding <<
" < " << diff;
3645 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
3646 view.
update(sleIssuance);
Provide a light-weight way to check active() before string formatting.
A generic endpoint for log messages.
static Sink & getNullSink()
Returns a Sink which does nothing.
Stream trace() const
Severity stream access functions.
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 void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
virtual void adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next)
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
constexpr value_type const & value() const
A currency issued by an account.
AccountID const & getIssuer() const
constexpr value_type value() const
Returns the underlying value.
constexpr MPTID const & getMptID() const
AccountID const & getIssuer() const
std::chrono::time_point< NetClock > time_point
std::chrono::duration< rep, period > duration
Number is a floating point type that can represent a wide range of values.
Number truncate() const noexcept
virtual Rules const & rules() const =0
Returns the tx processing rules.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
virtual bool open() const =0
Returns true if this reflects an open ledger.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
virtual std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const
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 bool holds() const noexcept
constexpr TIss const & get() const
std::string getFullText() const override
static constexpr std::uint64_t cMaxValue
void setIssuer(AccountID const &uIssuer)
std::uint64_t mantissa() const noexcept
bool negative() const noexcept
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
static int const cMaxOffset
Currency const & getCurrency() const
bool native() const noexcept
Asset const & asset() const
int exponent() const noexcept
AccountID const & getIssuer() const
std::shared_ptr< STLedgerEntry > const & ref
std::shared_ptr< STLedgerEntry const > const_pointer
bool isFieldPresent(SField const &field) const
T emplace_back(T... args)
bool internalDirFirst(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
bool internalDirNext(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
Keylet const & skip() noexcept
The index of the "short" skip list.
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Keylet const & amendments() noexcept
The index of the amendment table.
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 amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
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 mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
std::vector< SField const * > const & getPseudoAccountFields()
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, Issue const &issue, beast::Journal journal)
Any transactors that call addEmptyHolding() in doApply must call canAddHolding() in preflight with th...
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
FreezeHandling
Controls the treatment of frozen account balances.
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
SpendableHandling
Controls whether to include the account's full spendable balance.
bool isXRP(AccountID const &c)
TER canAddHolding(ReadView const &view, Asset const &asset)
std::uint8_t constexpr maxAssetCheckDepth
Maximum recursion depth for vault shares being put as an asset inside another vault; counted from 0.
std::map< uint256, NetClock::time_point > majorityAmendments_t
std::set< uint256 > getEnabledAmendments(ReadView const &view)
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
TER verifyValidDomain(ApplyView &view, AccountID const &account, uint256 domainID, beast::Journal j)
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
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 doWithdraw(ApplyView &view, STTx const &tx, AccountID const &senderAcct, AccountID const &dstAcct, AccountID const &sourceAcct, XRPAmount priorBalance, STAmount const &amount, beast::Journal j)
static TER accountSendMultiIOU(ApplyView &view, AccountID const &senderID, Issue const &issue, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee)
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
@ expired
List is expired, but has the largest non-pending sequence seen so far.
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Number root(Number f, unsigned d)
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
STAmount multiply(STAmount const &amount, Rate const &rate)
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
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)
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
STAmount creditLimit(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const ¤cy)
Calculate the maximum amount of IOUs that an account can hold.
static TER rippleCreditIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleSendMultiMPT(ApplyView &view, AccountID const &senderID, MPTIssue const &mptIssue, MultiplePaymentDestinations const &receivers, STAmount &actual, beast::Journal j, WaiveTransferFee waiveFee)
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
@ current
This was a new validation and was added.
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, char const *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
static std::uint32_t confineOwnerCount(std::uint32_t current, std::int32_t adjustment, std::optional< AccountID > const &id=std::nullopt, beast::Journal j=beast::Journal{beast::Journal::getNullSink()})
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
static TER rippleSendMultiIOU(ApplyView &view, AccountID const &senderID, Issue const &issue, MultiplePaymentDestinations const &receivers, STAmount &actual, beast::Journal j, WaiveTransferFee waiveFee)
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.
static STAmount getTrustLineBalance(ReadView const &view, SLE::const_ref sle, AccountID const &account, Currency const ¤cy, AccountID const &issuer, bool includeOppositeLimit, beast::Journal j)
static TER withdrawToDestExceedsLimit(ReadView const &view, AccountID const &from, AccountID const &to, STAmount const &amount)
AuthHandling
Controls the treatment of unauthorized MPT balances.
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
static SLE::const_pointer getLineIfUsable(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
majorityAmendments_t getMajorityAmendments(ReadView const &view)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
STAmount creditBalance(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const ¤cy)
Returns the amount of IOUs issued by issuer that are held by an account.
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
constexpr std::uint32_t const tfMPTUnauthorize
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
AccountID const & noAccount()
A placeholder for empty accounts.
static bool updateTrustLine(ApplyView &view, SLE::pointer state, bool bSenderHigh, AccountID const &sender, STAmount const &before, STAmount const &after, beast::Journal j)
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
bool isTesSuccess(TER x) noexcept
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
LedgerEntryType
Identifiers for on-ledger objects.
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecNO_LINE_INSUF_RESERVE
@ tecINSUFFICIENT_RESERVE
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
static TER accountSendMultiMPT(ApplyView &view, AccountID const &senderID, MPTIssue const &mptIssue, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee)
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 canWithdraw(ReadView const &view, AccountID const &from, AccountID const &to, SLE::const_ref toSle, STAmount const &amount, bool hasDestinationTag)
Checks that can withdraw funds from an object to itself or a destination.
Expected< std::shared_ptr< SLE >, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
TER checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag)
Validates that the destination SLE and tag are valid.
TER accountSendMulti(ApplyView &view, AccountID const &senderID, Asset const &asset, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Like accountSend, except one account is sending multiple payments (with the same asset!...
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
bool isLPTokenFrozen(ReadView const &view, AccountID const &account, Issue const &asset, Issue const &asset2)
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< std::uint16_t > maxNodesToDelete=std::nullopt)
Cleanup owner directory entries on account delete.
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Represents a transfer rate.
A field with a type known at compile time.
Returns the RIPEMD-160 digest of the SHA256 hash of the message.
T time_since_epoch(T... args)