mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-30 07:25:50 +00:00
Introduce CheckCashMakesTrustLine amendment:
With this amendment, the CheckCash transaction creates a TrustLine if needed. The change is modeled after offer crossing. And, similar to offer crossing, cashing a check allows an account to exceed its trust line limit.
This commit is contained in:
committed by
manojsdoshi
parent
8d59c7dd40
commit
bf75094224
@@ -21,6 +21,7 @@
|
||||
#include <ripple/app/paths/Flow.h>
|
||||
#include <ripple/app/tx/impl/CashCheck.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/scope.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
@@ -89,13 +90,13 @@ CashCheck::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
// Only cash a check with this account as the destination.
|
||||
AccountID const dstId{(*sleCheck)[sfDestination]};
|
||||
AccountID const dstId = sleCheck->at(sfDestination);
|
||||
if (ctx.tx[sfAccount] != dstId)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Cashing a check with wrong Destination.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
AccountID const srcId{(*sleCheck)[sfAccount]};
|
||||
AccountID const srcId = sleCheck->at(sfAccount);
|
||||
if (srcId == dstId)
|
||||
{
|
||||
// They wrote a check to themselves. This should be caught when
|
||||
@@ -127,7 +128,7 @@ CashCheck::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
using duration = NetClock::duration;
|
||||
using timepoint = NetClock::time_point;
|
||||
auto const optExpiry = (*sleCheck)[~sfExpiration];
|
||||
auto const optExpiry = sleCheck->at(~sfExpiration);
|
||||
|
||||
// Expiration is defined in terms of the close time of the parent
|
||||
// ledger, because we definitively know the time that it closed but
|
||||
@@ -148,7 +149,7 @@ CashCheck::preclaim(PreclaimContext const& ctx)
|
||||
return optAmount ? *optAmount : tx[sfDeliverMin];
|
||||
}(ctx.tx)};
|
||||
|
||||
STAmount const sendMax{(*sleCheck)[sfSendMax]};
|
||||
STAmount const sendMax = sleCheck->at(sfSendMax);
|
||||
Currency const currency{value.getCurrency()};
|
||||
if (currency != sendMax.getCurrency())
|
||||
{
|
||||
@@ -172,7 +173,7 @@ CashCheck::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
STAmount availableFunds{accountFunds(
|
||||
ctx.view,
|
||||
(*sleCheck)[sfAccount],
|
||||
sleCheck->at(sfAccount),
|
||||
value,
|
||||
fhZERO_IF_FROZEN,
|
||||
ctx.j)};
|
||||
@@ -197,7 +198,9 @@ CashCheck::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleTrustLine =
|
||||
ctx.view.read(keylet::line(dstId, issuerId, currency));
|
||||
if (!sleTrustLine)
|
||||
|
||||
if (!sleTrustLine &&
|
||||
!ctx.view.rules().enabled(featureCheckCashMakesTrustLine))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Cannot cash check for IOU without trustline.";
|
||||
@@ -213,15 +216,22 @@ CashCheck::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_ISSUER;
|
||||
}
|
||||
|
||||
if ((*sleIssuer)[sfFlags] & lsfRequireAuth)
|
||||
if (sleIssuer->at(sfFlags) & lsfRequireAuth)
|
||||
{
|
||||
if (!sleTrustLine)
|
||||
{
|
||||
// We can only create a trust line if the issuer does not
|
||||
// have requireAuth set.
|
||||
return tecNO_AUTH;
|
||||
}
|
||||
|
||||
// Entries have a canonical representation, determined by a
|
||||
// lexicographical "greater than" comparison employing strict
|
||||
// weak ordering. Determine which entry we need to access.
|
||||
bool const canonical_gt(dstId > issuerId);
|
||||
|
||||
bool const is_authorized(
|
||||
(*sleTrustLine)[sfFlags] &
|
||||
sleTrustLine->at(sfFlags) &
|
||||
(canonical_gt ? lsfLowAuth : lsfHighAuth));
|
||||
|
||||
if (!is_authorized)
|
||||
@@ -286,9 +296,10 @@ CashCheck::doApply()
|
||||
auto viewJ = ctx_.app.journal("View");
|
||||
auto const optDeliverMin = ctx_.tx[~sfDeliverMin];
|
||||
bool const doFix1623{ctx_.view().rules().enabled(fix1623)};
|
||||
|
||||
if (srcId != account_)
|
||||
{
|
||||
STAmount const sendMax{sleCheck->getFieldAmount(sfSendMax)};
|
||||
STAmount const sendMax = sleCheck->at(sfSendMax);
|
||||
|
||||
// Flow() doesn't do XRP to XRP transfers.
|
||||
if (sendMax.native())
|
||||
@@ -334,19 +345,106 @@ CashCheck::doApply()
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let flow() do the heavy lifting on a check for an IOU.
|
||||
//
|
||||
// Note that for DeliverMin we don't know exactly how much
|
||||
// currency we want flow to deliver. We can't ask for the
|
||||
// maximum possible currency because there might be a gateway
|
||||
// transfer rate to account for. Since the transfer rate cannot
|
||||
// exceed 200%, we use 1/2 maxValue as our limit.
|
||||
STAmount const flowDeliver{
|
||||
optDeliverMin
|
||||
? STAmount{optDeliverMin->issue(), STAmount::cMaxValue / 2, STAmount::cMaxOffset}
|
||||
: static_cast<STAmount>(ctx_.tx[sfAmount])};
|
||||
optDeliverMin ? STAmount(
|
||||
optDeliverMin->issue(),
|
||||
STAmount::cMaxValue / 2,
|
||||
STAmount::cMaxOffset)
|
||||
: ctx_.tx.getFieldAmount(sfAmount)};
|
||||
|
||||
// Call the payment engine's flow() to do the actual work.
|
||||
// If a trust line does not exist yet create one.
|
||||
Issue const& trustLineIssue = flowDeliver.issue();
|
||||
AccountID const issuer = flowDeliver.getIssuer();
|
||||
AccountID const truster = issuer == account_ ? srcId : account_;
|
||||
Keylet const trustLineKey = keylet::line(truster, trustLineIssue);
|
||||
bool const destLow = issuer > account_;
|
||||
|
||||
bool const checkCashMakesTrustLine =
|
||||
psb.rules().enabled(featureCheckCashMakesTrustLine);
|
||||
|
||||
if (checkCashMakesTrustLine && !psb.exists(trustLineKey))
|
||||
{
|
||||
// 1. Can the check casher meet the reserve for the trust line?
|
||||
// 2. Create trust line between destination (this) account
|
||||
// and the issuer.
|
||||
// 3. Apply correct noRipple settings on trust line. Use...
|
||||
// a. this (destination) account and
|
||||
// b. issuing account (not sending account).
|
||||
|
||||
// Can the account cover the trust line's reserve?
|
||||
if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)};
|
||||
mPriorBalance < psb.fees().accountReserve(ownerCount + 1))
|
||||
{
|
||||
JLOG(j_.trace()) << "Trust line does not exist. "
|
||||
"Insufficent reserve to create line.";
|
||||
|
||||
return tecNO_LINE_INSUF_RESERVE;
|
||||
}
|
||||
|
||||
Currency const currency = flowDeliver.getCurrency();
|
||||
STAmount initialBalance(flowDeliver.issue());
|
||||
initialBalance.setIssuer(noAccount());
|
||||
|
||||
// clang-format off
|
||||
if (TER const ter = trustCreate(
|
||||
psb, // payment sandbox
|
||||
destLow, // is dest low?
|
||||
issuer, // source
|
||||
account_, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDst, // Account to add to
|
||||
false, // authorize account
|
||||
(sleDst->getFlags() & lsfDefaultRipple) == 0,
|
||||
false, // freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, account_), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
viewJ); // journal
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// Note that we _don't_ need to be careful about destroying
|
||||
// the trust line if the check cashing fails. The transaction
|
||||
// machinery will automatically clean it up.
|
||||
}
|
||||
|
||||
// Since the destination is signing the check, they clearly want
|
||||
// the funds even if their new total funds would exceed the limit
|
||||
// on their trust line. So we tweak the trust line limits before
|
||||
// calling flow and then restore the trust line limits afterwards.
|
||||
auto const sleTrustLine = psb.peek(trustLineKey);
|
||||
if (!sleTrustLine)
|
||||
return tecNO_LINE;
|
||||
|
||||
SF_AMOUNT const& tweakedLimit = destLow ? sfLowLimit : sfHighLimit;
|
||||
STAmount const savedLimit = sleTrustLine->at(tweakedLimit);
|
||||
|
||||
// Make sure the tweaked limits are restored when we leave scope.
|
||||
scope_exit fixup(
|
||||
[&psb, &trustLineKey, &tweakedLimit, &savedLimit]() {
|
||||
if (auto const sleTrustLine = psb.peek(trustLineKey))
|
||||
sleTrustLine->at(tweakedLimit) = savedLimit;
|
||||
});
|
||||
|
||||
if (checkCashMakesTrustLine)
|
||||
{
|
||||
// Set the trust line limit to the highest possible value
|
||||
// while flow runs.
|
||||
STAmount const bigAmount(
|
||||
trustLineIssue, STAmount::cMaxValue, STAmount::cMaxOffset);
|
||||
sleTrustLine->at(tweakedLimit) = bigAmount;
|
||||
}
|
||||
|
||||
// Let flow() do the heavy lifting on a check for an IOU.
|
||||
auto const result = flow(
|
||||
psb,
|
||||
flowDeliver,
|
||||
@@ -376,10 +474,14 @@ CashCheck::doApply()
|
||||
<< "flow did not produce DeliverMin.";
|
||||
return tecPATH_PARTIAL;
|
||||
}
|
||||
if (doFix1623)
|
||||
if (doFix1623 && !checkCashMakesTrustLine)
|
||||
// Set the delivered_amount metadata.
|
||||
ctx_.deliver(result.actualAmountOut);
|
||||
}
|
||||
// Set the delivered amount metadata in all cases, not just
|
||||
// for DeliverMin.
|
||||
if (checkCashMakesTrustLine)
|
||||
ctx_.deliver(result.actualAmountOut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +489,7 @@ CashCheck::doApply()
|
||||
// check link from destination directory.
|
||||
if (srcId != account_)
|
||||
{
|
||||
std::uint64_t const page{(*sleCheck)[sfDestinationNode]};
|
||||
std::uint64_t const page = {sleCheck->at(sfDestinationNode)};
|
||||
if (!ctx_.view().dirRemove(
|
||||
keylet::ownerDir(account_), page, sleCheck->key(), true))
|
||||
{
|
||||
@@ -397,7 +499,7 @@ CashCheck::doApply()
|
||||
}
|
||||
// Remove check from check owner's directory.
|
||||
{
|
||||
std::uint64_t const page{(*sleCheck)[sfOwnerNode]};
|
||||
std::uint64_t const page = {sleCheck->at(sfOwnerNode)};
|
||||
if (!ctx_.view().dirRemove(
|
||||
keylet::ownerDir(srcId), page, sleCheck->key(), true))
|
||||
{
|
||||
|
||||
@@ -117,6 +117,7 @@ class FeatureCollections
|
||||
"FlowSortStrands",
|
||||
"fixSTAmountCanonicalize",
|
||||
"fixRmSmallIncreasedQOffers",
|
||||
"CheckCashMakesTrustLine",
|
||||
};
|
||||
|
||||
std::vector<uint256> features;
|
||||
@@ -378,6 +379,7 @@ extern uint256 const featureTicketBatch;
|
||||
extern uint256 const featureFlowSortStrands;
|
||||
extern uint256 const fixSTAmountCanonicalize;
|
||||
extern uint256 const fixRmSmallIncreasedQOffers;
|
||||
extern uint256 const featureCheckCashMakesTrustLine;
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ detail::supportedAmendments()
|
||||
"FlowSortStrands",
|
||||
"fixSTAmountCanonicalize",
|
||||
"fixRmSmallIncreasedQOffers",
|
||||
"CheckCashMakesTrustLine",
|
||||
};
|
||||
return supported;
|
||||
}
|
||||
@@ -192,7 +193,8 @@ uint256 const
|
||||
featureTicketBatch = *getRegisteredFeature("TicketBatch"),
|
||||
featureFlowSortStrands = *getRegisteredFeature("FlowSortStrands"),
|
||||
fixSTAmountCanonicalize = *getRegisteredFeature("fixSTAmountCanonicalize"),
|
||||
fixRmSmallIncreasedQOffers = *getRegisteredFeature("fixRmSmallIncreasedQOffers");
|
||||
fixRmSmallIncreasedQOffers = *getRegisteredFeature("fixRmSmallIncreasedQOffers"),
|
||||
featureCheckCashMakesTrustLine = *getRegisteredFeature("CheckCashMakesTrustLine");
|
||||
|
||||
// The following amendments have been active for at least two years. Their
|
||||
// pre-amendment code has been removed and the identifiers are deprecated.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -348,8 +348,9 @@ Env::postconditions(JTx const& jt, TER ter, bool didApply)
|
||||
if (jt.ter &&
|
||||
!test.expect(
|
||||
ter == *jt.ter,
|
||||
"apply: " + transToken(ter) + " (" + transHuman(ter) + ") != " +
|
||||
transToken(*jt.ter) + " (" + transHuman(*jt.ter) + ")"))
|
||||
"apply: Got " + transToken(ter) + " (" + transHuman(ter) +
|
||||
"); Expected " + transToken(*jt.ter) + " (" +
|
||||
transHuman(*jt.ter) + ")"))
|
||||
{
|
||||
test.log << pretty(jt.jv) << std::endl;
|
||||
// Don't check postconditions if
|
||||
|
||||
Reference in New Issue
Block a user