fix finalAmount + guard dest Issuer

This commit is contained in:
Denis Angell
2023-01-29 05:49:54 -05:00
committed by Richard Holland
parent 31da4bf8d4
commit f8591d0fd6

View File

@@ -811,7 +811,8 @@ trustTransferLockedBalance(
bool srcHigh = srcAccID > issuerAccID; bool srcHigh = srcAccID > issuerAccID;
bool dstHigh = dstAccID > issuerAccID; bool dstHigh = dstAccID > issuerAccID;
bool isIssuer = issuerAccID == srcAccID; bool srcIssuer = issuerAccID == srcAccID;
bool dstIssuer = issuerAccID == dstAccID;
// check for freezing, auth, no ripple and TL sanity // check for freezing, auth, no ripple and TL sanity
{ {
@@ -825,13 +826,36 @@ trustTransferLockedBalance(
return result; return result;
} }
// dstLow XNOR srcLow tells us if we need to flip the balance amount
// on the destination line
bool flipDstAmt = !((dstHigh && srcHigh) || (!dstHigh && !srcHigh));
// default dstAmount to amount
auto dstAmt = amount;
// if tx acct not source issuer or dest issuer
// and xfer rate is not parity
if ((!srcIssuer || !dstIssuer) && lXferRate != parityRate)
{
// compute transfer fee, if any
auto const xferFee = amount.value() -
divideRound(
amount,
lXferRate,
amount.issue(),
true);
// compute balance to transfer
dstAmt = amount.value() - xferFee;
}
// ensure source line exists // ensure source line exists
Keylet klSrcLine{keylet::line(srcAccID, issuerAccID, currency)}; Keylet klSrcLine{keylet::line(srcAccID, issuerAccID, currency)};
SLEPtr sleSrcLine = peek(klSrcLine); SLEPtr sleSrcLine = peek(klSrcLine);
// source account is not issuer use locked balance // if source account is not issuer
if (!isIssuer) if (!srcIssuer)
{ {
// if source account has no trust line - fail
if (!sleSrcLine) if (!sleSrcLine)
return tecNO_LINE; return tecNO_LINE;
@@ -916,114 +940,111 @@ trustTransferLockedBalance(
} }
} }
// dstLow XNOR srcLow tells us if we need to flip the balance amount
// on the destination line
bool flipDstAmt = !((dstHigh && srcHigh) || (!dstHigh && !srcHigh));
// default to amount
auto dstAmt = amount;
// if transfer rate
if (lXferRate != parityRate)
{
// compute transfer fee, if any
auto const xferFee = amount.value() -
divideRound(amount, lXferRate, amount.issue(), true);
// compute balance to transfer
dstAmt = amount.value() - xferFee;
}
// check for a destination line // check for a destination line
Keylet klDstLine = keylet::line(dstAccID, issuerAccID, currency); Keylet klDstLine = keylet::line(dstAccID, issuerAccID, currency);
SLEPtr sleDstLine = peek(klDstLine); SLEPtr sleDstLine = peek(klDstLine);
if (!sleDstLine) // if dest account is not issuer
if (!dstIssuer)
{ {
// in most circumstances a missing destination line is a deal breaker // if dest acct has no trustline
if (actingAccID != dstAccID && srcAccID != dstAccID) if (!sleDstLine)
return tecNO_LINE;
STAmount dstBalanceDrops = sleDstAcc->getFieldAmount(sfBalance);
// no dst line exists, we might be able to create one...
if (std::uint32_t const ownerCount = {sleDstAcc->at(sfOwnerCount)};
dstBalanceDrops < view.fees().accountReserve(ownerCount + 1))
return tecNO_LINE_INSUF_RESERVE;
// yes we can... we will
auto const finalDstAmt =
isIssuer ? dstAmt : flipDstAmt ? -dstAmt : dstAmt;
if constexpr (!dryRun)
{ {
// clang-format off // if tx acct is not dest acct and src acct is not dest acct
if (TER const ter = trustCreate( if (actingAccID != dstAccID && srcAccID != dstAccID)
view, return tecNO_LINE;
!dstHigh, // is dest low?
issuerAccID, // source STAmount dstBalanceDrops = sleDstAcc->getFieldAmount(sfBalance);
dstAccID, // destination
klDstLine.key, // ledger index // no dst line exists, we might be able to create one...
sleDstAcc, // Account to add to if (std::uint32_t const ownerCount = {sleDstAcc->at(sfOwnerCount)};
false, // authorize account dstBalanceDrops < view.fees().accountReserve(ownerCount + 1))
(sleDstAcc->getFlags() & lsfDefaultRipple) == 0, return tecNO_LINE_INSUF_RESERVE;
false, // freeze trust line
finalDstAmt, // initial balance // compute final destination amount
Issue(currency, dstAccID), // limit of zero auto const finalDstAmt = flipDstAmt ? -dstAmt : dstAmt;
0, // quality in // create destination trust line
0, // quality out if constexpr (!dryRun)
j); // journal
!isTesSuccess(ter))
{ {
return ter; // clang-format off
if (TER const ter = trustCreate(
view,
!dstHigh, // is dest low?
issuerAccID, // source
dstAccID, // destination
klDstLine.key, // ledger index
sleDstAcc, // Account to add to
false, // authorize account
(sleDstAcc->getFlags() & lsfDefaultRipple) == 0,
false, // freeze trust line
finalDstAmt, // initial balance
Issue(currency, dstAccID), // limit of zero
0, // quality in
0, // quality out
j); // journal
!isTesSuccess(ter))
{
return ter;
}
} }
// clang-format on
} }
// clang-format on else
}
else
{
// the dst line does exist, and it would have been checked above
// in trustTransferAllowed for NoRipple and Freeze flags
// check the limit
STAmount dstLimit =
dstHigh ? (*sleDstLine)[sfHighLimit] : (*sleDstLine)[sfLowLimit];
STAmount priorBalance =
dstHigh ? -((*sleDstLine)[sfBalance]) : (*sleDstLine)[sfBalance];
STAmount finalBalance = priorBalance + dstAmt;
if (finalBalance < priorBalance)
{ {
JLOG(j.warn()) << "trustTransferLockedBalance resulted in a " // dest trust line does exist
"lower/equal final balance on dest line"; // checked NoRipple and Freeze flags in trustTransferAllowed
return tecINTERNAL;
// check the limit
STAmount dstLimit =
dstHigh ? (*sleDstLine)[sfHighLimit] : (*sleDstLine)[sfLowLimit];
// get prior balance
STAmount priorBalance =
dstHigh ? -((*sleDstLine)[sfBalance]) : (*sleDstLine)[sfBalance];
// combine prior with dest amount for final
STAmount finalBalance = priorBalance + dstAmt;
// if final is less than prior - fail
if (finalBalance < priorBalance)
{
JLOG(j.warn()) << "trustTransferLockedBalance resulted in a "
"lower/equal final balance on dest line";
return tecINTERNAL;
}
// if final is more than dest limit and tx acct is not dest acct - fail
if (finalBalance > dstLimit && actingAccID != dstAccID)
{
JLOG(j.trace()) << "trustTransferLockedBalance would increase dest "
"line above limit without permission";
return tecPATH_DRY;
}
// if there is significant precision loss - fail
if (!isAddable(priorBalance, dstAmt))
return tecPRECISION_LOSS;
// compute final balance to send - reverse sign for high dest
finalBalance = dstHigh ? -finalBalance : finalBalance;
// if not dry run - set dst line field
if constexpr (!dryRun)
sleDstLine->setFieldAmount(sfBalance, finalBalance);
} }
if (finalBalance > dstLimit && actingAccID != dstAccID)
{
JLOG(j.trace()) << "trustTransferLockedBalance would increase dest "
"line above limit without permission";
return tecPATH_DRY;
}
// check if there is significant precision loss
if (!isAddable(priorBalance, dstAmt))
return tecPRECISION_LOSS;
finalBalance =
isIssuer ? -finalBalance : dstHigh ? -finalBalance : finalBalance;
if constexpr (!dryRun)
sleDstLine->setFieldAmount(sfBalance, finalBalance);
} }
if constexpr (!dryRun) if constexpr (!dryRun)
{ {
static_assert(std::is_same<V, ApplyView>::value); static_assert(std::is_same<V, ApplyView>::value);
// check if source line ended up in default state and adjust owner count // if source account is not issuer
// if it did if (!srcIssuer)
if (!isIssuer)
{ {
// check if source line ended up in default state
if (isTrustDefault(sleSrcAcc, sleSrcLine)) if (isTrustDefault(sleSrcAcc, sleSrcLine))
{ {
// adjust owner count
uint32_t flags = sleSrcLine->getFieldU32(sfFlags); uint32_t flags = sleSrcLine->getFieldU32(sfFlags);
uint32_t fReserve{srcHigh ? lsfHighReserve : lsfLowReserve}; uint32_t fReserve{srcHigh ? lsfHighReserve : lsfLowReserve};
if (flags & fReserve) if (flags & fReserve)
@@ -1033,11 +1054,13 @@ trustTransferLockedBalance(
view.update(sleSrcAcc); view.update(sleSrcAcc);
} }
} }
// update source line
view.update(sleSrcLine); view.update(sleSrcLine);
} }
// a destination line already existed and was updated // if dest line exists
if (sleDstLine) if (sleDstLine)
// update dest line
view.update(sleDstLine); view.update(sleDstLine);
} }
return tesSUCCESS; return tesSUCCESS;