mirror of
https://github.com/XRPLF/rippled.git
synced 2026-01-25 09:05:24 +00:00
Compare commits
1 Commits
develop
...
ximinez/le
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6f3542b7e |
@@ -2167,6 +2167,105 @@ rippleSendIOU(
|
|||||||
return terResult;
|
return terResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class TAsset>
|
||||||
|
static TER
|
||||||
|
doSendMulti(
|
||||||
|
std::string const& name,
|
||||||
|
ApplyView& view,
|
||||||
|
AccountID const& senderID,
|
||||||
|
TAsset const& issue,
|
||||||
|
MultiplePaymentDestinations const& receivers,
|
||||||
|
STAmount& actual,
|
||||||
|
beast::Journal j,
|
||||||
|
WaiveTransferFee waiveFee,
|
||||||
|
// Don't pass back parameters that the caller already has
|
||||||
|
std::function<
|
||||||
|
TER(AccountID const& senderID,
|
||||||
|
AccountID const& receiverID,
|
||||||
|
STAmount const& amount,
|
||||||
|
bool checkIssuer)> doCredit,
|
||||||
|
std::function<
|
||||||
|
TER(AccountID const& issuer,
|
||||||
|
STAmount const& takeFromSender,
|
||||||
|
STAmount const& amount)> preMint = {})
|
||||||
|
{
|
||||||
|
// Use the same pattern for all the SendMulti functions to help avoid
|
||||||
|
// divergence and copy/paste errors.
|
||||||
|
auto const& issuer = issue.getIssuer();
|
||||||
|
|
||||||
|
// These values may not stay in sync
|
||||||
|
STAmount takeFromSender{issue};
|
||||||
|
actual = takeFromSender;
|
||||||
|
|
||||||
|
// Failures return immediately.
|
||||||
|
for (auto const& r : receivers)
|
||||||
|
{
|
||||||
|
auto const& receiverID = r.first;
|
||||||
|
STAmount amount{issue, r.second};
|
||||||
|
|
||||||
|
if (amount < beast::zero)
|
||||||
|
{
|
||||||
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we aren't sending anything or if the sender is the same as the
|
||||||
|
* receiver then we don't need to do anything.
|
||||||
|
*/
|
||||||
|
if (!amount || (senderID == receiverID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
XRPL_ASSERT(
|
||||||
|
!isXRP(receiverID),
|
||||||
|
("xrpl::"s + name + " : receiver is not XRP").c_str());
|
||||||
|
|
||||||
|
if (senderID == issuer || receiverID == issuer || issuer == noAccount())
|
||||||
|
{
|
||||||
|
if (preMint)
|
||||||
|
{
|
||||||
|
if (auto const ter = preMint(issuer, takeFromSender, amount))
|
||||||
|
return ter;
|
||||||
|
}
|
||||||
|
// Direct send: redeeming IOUs and/or sending own IOUs.
|
||||||
|
if (auto const ter = doCredit(senderID, receiverID, amount, false))
|
||||||
|
return ter;
|
||||||
|
actual += amount;
|
||||||
|
// Do not add amount to takeFromSender, because doCredit took
|
||||||
|
// it.
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sending 3rd party: transit.
|
||||||
|
|
||||||
|
// Calculate the amount to transfer accounting
|
||||||
|
// for any transfer fees if the fee is not waived:
|
||||||
|
STAmount actualSend =
|
||||||
|
(waiveFee == WaiveTransferFee::Yes || issue.native())
|
||||||
|
? amount
|
||||||
|
: multiply(amount, transferRate(view, amount));
|
||||||
|
actual += actualSend;
|
||||||
|
takeFromSender += actualSend;
|
||||||
|
|
||||||
|
JLOG(j.debug()) << name << "> " << to_string(senderID) << " - > "
|
||||||
|
<< to_string(receiverID)
|
||||||
|
<< " : deliver=" << amount.getFullText()
|
||||||
|
<< " cost=" << actualSend.getFullText();
|
||||||
|
|
||||||
|
if (TER const terResult = doCredit(issuer, receiverID, amount, true))
|
||||||
|
return terResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (senderID != issuer && takeFromSender)
|
||||||
|
{
|
||||||
|
if (TER const terResult =
|
||||||
|
doCredit(senderID, issuer, takeFromSender, true))
|
||||||
|
return terResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// Send regardless of limits.
|
// Send regardless of limits.
|
||||||
// --> receivers: Amount/currency/issuer to deliver to receivers.
|
// --> receivers: Amount/currency/issuer to deliver to receivers.
|
||||||
// <-- saActual: Amount actually cost to sender. Sender pays fees.
|
// <-- saActual: Amount actually cost to sender. Sender pays fees.
|
||||||
@@ -2180,72 +2279,28 @@ rippleSendMultiIOU(
|
|||||||
beast::Journal j,
|
beast::Journal j,
|
||||||
WaiveTransferFee waiveFee)
|
WaiveTransferFee waiveFee)
|
||||||
{
|
{
|
||||||
auto const& issuer = issue.getIssuer();
|
|
||||||
|
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP");
|
!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP");
|
||||||
|
|
||||||
// These may diverge
|
auto doCredit = [&view, j](
|
||||||
STAmount takeFromSender{issue};
|
AccountID const& senderID,
|
||||||
actual = takeFromSender;
|
AccountID const& receiverID,
|
||||||
|
STAmount const& amount,
|
||||||
|
bool checkIssuer) {
|
||||||
|
return rippleCreditIOU(
|
||||||
|
view, senderID, receiverID, amount, checkIssuer, j);
|
||||||
|
};
|
||||||
|
|
||||||
// Failures return immediately.
|
return doSendMulti(
|
||||||
for (auto const& r : receivers)
|
"rippleSendMultiIOU",
|
||||||
{
|
view,
|
||||||
auto const& receiverID = r.first;
|
senderID,
|
||||||
STAmount amount{issue, r.second};
|
issue,
|
||||||
|
receivers,
|
||||||
/* If we aren't sending anything or if the sender is the same as the
|
actual,
|
||||||
* receiver then we don't need to do anything.
|
j,
|
||||||
*/
|
waiveFee,
|
||||||
if (!amount || (senderID == receiverID))
|
doCredit);
|
||||||
continue;
|
|
||||||
|
|
||||||
XRPL_ASSERT(
|
|
||||||
!isXRP(receiverID),
|
|
||||||
"xrpl::rippleSendMultiIOU : receiver is not XRP");
|
|
||||||
|
|
||||||
if (senderID == issuer || receiverID == issuer || issuer == noAccount())
|
|
||||||
{
|
|
||||||
// Direct send: redeeming IOUs and/or sending own IOUs.
|
|
||||||
if (auto const ter = rippleCreditIOU(
|
|
||||||
view, senderID, receiverID, amount, false, j))
|
|
||||||
return ter;
|
|
||||||
actual += amount;
|
|
||||||
// Do not add amount to takeFromSender, because rippleCreditIOU took
|
|
||||||
// it.
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sending 3rd party IOUs: transit.
|
|
||||||
|
|
||||||
// Calculate the amount to transfer accounting
|
|
||||||
// for any transfer fees if the fee is not waived:
|
|
||||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
|
|
||||||
? amount
|
|
||||||
: multiply(amount, transferRate(view, issuer));
|
|
||||||
actual += actualSend;
|
|
||||||
takeFromSender += actualSend;
|
|
||||||
|
|
||||||
JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID)
|
|
||||||
<< " - > " << to_string(receiverID)
|
|
||||||
<< " : deliver=" << amount.getFullText()
|
|
||||||
<< " cost=" << actual.getFullText();
|
|
||||||
|
|
||||||
if (TER const terResult =
|
|
||||||
rippleCreditIOU(view, issuer, receiverID, amount, true, j))
|
|
||||||
return terResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (senderID != issuer && takeFromSender)
|
|
||||||
{
|
|
||||||
if (TER const terResult = rippleCreditIOU(
|
|
||||||
view, senderID, issuer, takeFromSender, true, j))
|
|
||||||
return terResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tesSUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
@@ -2386,9 +2441,9 @@ accountSendMultiIOU(
|
|||||||
"xrpl::accountSendMultiIOU",
|
"xrpl::accountSendMultiIOU",
|
||||||
"multiple recipients provided");
|
"multiple recipients provided");
|
||||||
|
|
||||||
|
STAmount actual;
|
||||||
if (!issue.native())
|
if (!issue.native())
|
||||||
{
|
{
|
||||||
STAmount actual;
|
|
||||||
JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID)
|
JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID)
|
||||||
<< " sending " << receivers.size() << " IOUs";
|
<< " sending " << receivers.size() << " IOUs";
|
||||||
|
|
||||||
@@ -2417,6 +2472,97 @@ accountSendMultiIOU(
|
|||||||
<< sender_bal << ") -> " << receivers.size() << " receivers.";
|
<< sender_bal << ") -> " << receivers.size() << " receivers.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto doCredit = [&view, &sender, &receivers, j](
|
||||||
|
AccountID const& senderID,
|
||||||
|
AccountID const& receiverID,
|
||||||
|
STAmount const& amount,
|
||||||
|
bool /*checkIssuer*/) -> TER {
|
||||||
|
if (!senderID)
|
||||||
|
{
|
||||||
|
SLE::pointer receiver = receiverID != beast::zero
|
||||||
|
? view.peek(keylet::account(receiverID))
|
||||||
|
: SLE::pointer();
|
||||||
|
|
||||||
|
if (auto stream = j.trace())
|
||||||
|
{
|
||||||
|
std::string receiver_bal("-");
|
||||||
|
|
||||||
|
if (receiver)
|
||||||
|
receiver_bal =
|
||||||
|
receiver->getFieldAmount(sfBalance).getFullText();
|
||||||
|
|
||||||
|
stream << "accountSendMultiIOU> " << to_string(senderID)
|
||||||
|
<< " -> " << to_string(receiverID) << " ("
|
||||||
|
<< receiver_bal << ") : " << amount.getFullText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver)
|
||||||
|
{
|
||||||
|
// Increment XRP balance.
|
||||||
|
auto const rcvBal = receiver->getFieldAmount(sfBalance);
|
||||||
|
receiver->setFieldAmount(sfBalance, rcvBal + amount);
|
||||||
|
view.creditHook(xrpAccount(), receiverID, amount, -rcvBal);
|
||||||
|
|
||||||
|
view.update(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto stream = j.trace())
|
||||||
|
{
|
||||||
|
std::string receiver_bal("-");
|
||||||
|
|
||||||
|
if (receiver)
|
||||||
|
receiver_bal =
|
||||||
|
receiver->getFieldAmount(sfBalance).getFullText();
|
||||||
|
|
||||||
|
stream << "accountSendMultiIOU< " << to_string(senderID)
|
||||||
|
<< " -> " << to_string(receiverID) << " ("
|
||||||
|
<< receiver_bal << ") : " << amount.getFullText();
|
||||||
|
}
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
// Sender
|
||||||
|
if (sender)
|
||||||
|
{
|
||||||
|
if (sender->getFieldAmount(sfBalance) < amount)
|
||||||
|
{
|
||||||
|
return TER{tecFAILED_PROCESSING};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto const sndBal = sender->getFieldAmount(sfBalance);
|
||||||
|
view.creditHook(senderID, xrpAccount(), amount, sndBal);
|
||||||
|
|
||||||
|
// Decrement XRP balance.
|
||||||
|
sender->setFieldAmount(sfBalance, sndBal - amount);
|
||||||
|
view.update(sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto stream = j.trace())
|
||||||
|
{
|
||||||
|
std::string sender_bal("-");
|
||||||
|
|
||||||
|
if (sender)
|
||||||
|
sender_bal = sender->getFieldAmount(sfBalance).getFullText();
|
||||||
|
|
||||||
|
stream << "accountSendMultiIOU< " << to_string(senderID) << " ("
|
||||||
|
<< sender_bal << ") -> " << receivers.size()
|
||||||
|
<< " receivers.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
};
|
||||||
|
return doSendMulti(
|
||||||
|
"accountSendMultiIOU",
|
||||||
|
view,
|
||||||
|
senderID,
|
||||||
|
issue,
|
||||||
|
receivers,
|
||||||
|
actual,
|
||||||
|
j,
|
||||||
|
waiveFee,
|
||||||
|
doCredit);
|
||||||
|
|
||||||
// Failures return immediately.
|
// Failures return immediately.
|
||||||
STAmount takeFromSender{issue};
|
STAmount takeFromSender{issue};
|
||||||
for (auto const& r : receivers)
|
for (auto const& r : receivers)
|
||||||
@@ -2648,90 +2794,51 @@ rippleSendMultiMPT(
|
|||||||
beast::Journal j,
|
beast::Journal j,
|
||||||
WaiveTransferFee waiveFee)
|
WaiveTransferFee waiveFee)
|
||||||
{
|
{
|
||||||
// Safe to get MPT since rippleSendMultiMPT is only called by
|
|
||||||
// accountSendMultiMPT
|
|
||||||
auto const& issuer = mptIssue.getIssuer();
|
|
||||||
|
|
||||||
auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||||
if (!sle)
|
if (!sle)
|
||||||
return tecOBJECT_NOT_FOUND;
|
return tecOBJECT_NOT_FOUND;
|
||||||
|
|
||||||
// These may diverge
|
auto preMint = [&](AccountID const& issuer,
|
||||||
STAmount takeFromSender{mptIssue};
|
STAmount const& takeFromSender,
|
||||||
actual = takeFromSender;
|
STAmount const& amount) -> TER {
|
||||||
|
// if sender is issuer, check that the new OutstandingAmount will
|
||||||
for (auto const& r : receivers)
|
// not exceed MaximumAmount
|
||||||
{
|
if (senderID == issuer)
|
||||||
auto const& receiverID = r.first;
|
|
||||||
STAmount amount{mptIssue, r.second};
|
|
||||||
|
|
||||||
if (amount < beast::zero)
|
|
||||||
{
|
{
|
||||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
XRPL_ASSERT_PARTS(
|
||||||
|
takeFromSender == beast::zero,
|
||||||
|
"rippler::rippleSendMultiMPT",
|
||||||
|
"sender == issuer, takeFromSender == zero");
|
||||||
|
auto const sendAmount = amount.mpt().value();
|
||||||
|
auto const maximumAmount =
|
||||||
|
sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||||
|
if (sendAmount > maximumAmount ||
|
||||||
|
sle->getFieldU64(sfOutstandingAmount) >
|
||||||
|
maximumAmount - sendAmount)
|
||||||
|
return tecPATH_DRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we aren't sending anything or if the sender is the same as the
|
return tesSUCCESS;
|
||||||
* receiver then we don't need to do anything.
|
};
|
||||||
*/
|
auto doCredit = [&view, j](
|
||||||
if (!amount || (senderID == receiverID))
|
AccountID const& senderID,
|
||||||
continue;
|
AccountID const& receiverID,
|
||||||
|
STAmount const& amount,
|
||||||
|
bool) {
|
||||||
|
return rippleCreditMPT(view, senderID, receiverID, amount, j);
|
||||||
|
};
|
||||||
|
|
||||||
if (senderID == issuer || receiverID == issuer)
|
return doSendMulti(
|
||||||
{
|
"rippleSendMultiMPT",
|
||||||
// if sender is issuer, check that the new OutstandingAmount will
|
view,
|
||||||
// not exceed MaximumAmount
|
senderID,
|
||||||
if (senderID == issuer)
|
mptIssue,
|
||||||
{
|
receivers,
|
||||||
XRPL_ASSERT_PARTS(
|
actual,
|
||||||
takeFromSender == beast::zero,
|
j,
|
||||||
"rippler::rippleSendMultiMPT",
|
waiveFee,
|
||||||
"sender == issuer, takeFromSender == zero");
|
doCredit,
|
||||||
auto const sendAmount = amount.mpt().value();
|
preMint);
|
||||||
auto const maximumAmount =
|
|
||||||
sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
|
||||||
if (sendAmount > maximumAmount ||
|
|
||||||
sle->getFieldU64(sfOutstandingAmount) >
|
|
||||||
maximumAmount - sendAmount)
|
|
||||||
return tecPATH_DRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct send: redeeming MPTs and/or sending own MPTs.
|
|
||||||
if (auto const ter =
|
|
||||||
rippleCreditMPT(view, senderID, receiverID, amount, j))
|
|
||||||
return ter;
|
|
||||||
actual += amount;
|
|
||||||
// Do not add amount to takeFromSender, because rippleCreditMPT took
|
|
||||||
// it
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sending 3rd party MPTs: transit.
|
|
||||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
|
|
||||||
? amount
|
|
||||||
: multiply(
|
|
||||||
amount,
|
|
||||||
transferRate(view, amount.get<MPTIssue>().getMptID()));
|
|
||||||
actual += actualSend;
|
|
||||||
takeFromSender += actualSend;
|
|
||||||
|
|
||||||
JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID)
|
|
||||||
<< " - > " << to_string(receiverID)
|
|
||||||
<< " : deliver=" << amount.getFullText()
|
|
||||||
<< " cost=" << actualSend.getFullText();
|
|
||||||
|
|
||||||
if (auto const terResult =
|
|
||||||
rippleCreditMPT(view, issuer, receiverID, amount, j))
|
|
||||||
return terResult;
|
|
||||||
}
|
|
||||||
if (senderID != issuer && takeFromSender)
|
|
||||||
{
|
|
||||||
if (TER const terResult =
|
|
||||||
rippleCreditMPT(view, senderID, issuer, takeFromSender, j))
|
|
||||||
return terResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tesSUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static TER
|
static TER
|
||||||
|
|||||||
Reference in New Issue
Block a user