started amendment

This commit is contained in:
Richard Holland
2022-03-22 13:49:41 +00:00
parent 1a8eb5e6e3
commit d94b231ff1
8 changed files with 192 additions and 4 deletions

View File

@@ -106,8 +106,25 @@ EscrowCreate::preflight(PreflightContext const& ctx)
if (!isTesSuccess(ret)) if (!isTesSuccess(ret))
return ret; return ret;
if (!isXRP(ctx.tx[sfAmount])) STAmount const amount {ctx.tx[sfAmount]};
return temBAD_AMOUNT; if (!isXRP(amount))
{
if (!ctx.view.rules().enabled(featurePaychanAndEscrowForTokens))
return temBAD_AMOUNT;
if (!isLegalNet(amount))
return temBAD_AMOUNT;
if (isFakeXRP(amount))
return temBAD_CURRENCY;
if (account == amount.getIssuer())
{
JLOG(ctx.j.trace())
<< "Malformed transaction: Cannot escrow own tokens to self.";
return temDST_IS_SRC;
}
}
if (ctx.tx[sfAmount] <= beast::zero) if (ctx.tx[sfAmount] <= beast::zero)
return temBAD_AMOUNT; return temBAD_AMOUNT;
@@ -199,7 +216,12 @@ EscrowCreate::doApply()
if (!sle) if (!sle)
return tefINTERNAL; return tefINTERNAL;
STAmount const amount {ctx.tx[sfAmount]};
std::shared_ptr<SLE> sleLine;
// Check reserve and funds availability // Check reserve and funds availability
if (isXRP(amount))
{ {
auto const balance = STAmount((*sle)[sfBalance]).xrp(); auto const balance = STAmount((*sle)[sfBalance]).xrp();
auto const reserve = auto const reserve =
@@ -211,6 +233,52 @@ EscrowCreate::doApply()
if (balance < reserve + STAmount(ctx_.tx[sfAmount]).xrp()) if (balance < reserve + STAmount(ctx_.tx[sfAmount]).xrp())
return tecUNFUNDED; return tecUNFUNDED;
} }
else if (ctx.view.rules().enabled(featurePaychanAndEscrowForTokens))
{
// find the user's trustline
auto const currency = amount.getCurrency();
auto const issuer = amount.getIssuer();
if (!ctx_.view.peek(keylet::account(issuer)))
{
JLOG(ctx_.j.warn
}
if (isGlobalFrozen(ctx_.view(), issuer))
{
JLOG(ctx_.j.warn()) << "Creating escrow for frozen asset";
return tecFROZEN;
}
sleLine = ctx_.view().peek(keylet::line(account, issuer, currency));
if (!sleLine)
return tecUNFUNDED_PAYMENT;
if (sleLine->isFlag(ctx_.tx[sfDestination] > issuer ? lsfHighFreeze : lsfLowFreeze))
{
JLOG(ctx_.j.warn()) << "Creating escrow for destination frozen trustline";
return tecFROZEN;
}
bool high = account > issuer;
STAmount balance = (*sleLine)[sfBalance];
STAmount lockedBalance {sfLockedBalance};
if (sleLine->isFieldPresent(sfLockedBalance))
lockedBalance = (*sleLine)[sfLockedBalance];
auto spendableBalance = balance - lockedBalance;
if (high)
spendableBalance = -spendableBalance;
if (amount > spendableBalance)
return tecUNFUNDED;
}
else
return tecINTERNAL; // should never happen
// Check destination account // Check destination account
{ {
@@ -265,7 +333,26 @@ EscrowCreate::doApply()
} }
// Deduct owner's balance, increment owner count // Deduct owner's balance, increment owner count
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; if (isXRP(amount))
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
else if (ctx.view.rules().enabled(featurePaychanAndEscrowForTokens) && sleLine)
{
// update trustline to reflect locked up balance
auto const issuer = amount.getIssuer();
bool high = account > issuer;
STAmount lockedBalance;
if (sleLine->isFieldPresent(sfLockedBalance))
lockedBalance = (*sleLine)[sfLockedBalance];
lockedBalance += high ? -amount : amount;
sleLine->setFieldAmount(sfLockedBalance, lockedBalance);
ctx_.view().update(sleLine);
}
else
return tecINTERNAL; // should never happen
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
ctx_.view().update(sle); ctx_.view().update(sle);
@@ -489,7 +576,86 @@ EscrowFinish::doApply()
} }
// Transfer amount to destination // Transfer amount to destination
(*sled)[sfBalance] = (*sled)[sfBalance] + (*slep)[sfAmount]; auto amount = (*sled)[sfBalance];
if (isXRP(amount))
{
(*sled)[sfBalance] = (*sled)[sfBalance] + (*slep)[sfAmount];
}
else if (ctx.view.rules().enabled(featurePaychanAndEscrowForTokens))
{
// check if the destination has a trustline already
auto issuer = amount.getIssuer();
auto currency = amount.getCurrency();
// check the issuer exists
if (!ctx_.view().exits(keylet::account(issuer)))
{
JLOG(ctx_.j.warn())
<< "Cannot finish escrow for token from non-existent issuer: "
<< to_string(issuer);
return tecNO_ISSUER;
}
// check the trustline isn't frozen
if (isGlobalFrozen(ctx_.view(), issuer))
{
JLOG(ctx_.j.warn()) << "Cannot finish an escrow for frozen issuer";
return tecFROZEN;
}
// check the source line exists
auto sleSrcLine = ctx_.view().peek(keylet::line(account, issuer, currency));
if (!sleSrcLine)
{
JLOG(ctx_.j.error())
<< "Cannot finish an escrow where the source line does not exist: "
<< "acc: " << to_string(account) << " "
<< "iss: " << to_string(issuer) << " "
<< "cur: " << to_string(currency);
return tefINTERNAL;
}
Keylet k = keylet::line(destID, issuer, currency);
// check if the dest line exists, and if it doesn't create it if we're allowed to
auto sleDestLine = ctx_.view().peek(k);
if (!sleDestLine)
{
// if a line doesn't already exist between issuer and destination then
// such a line can now be created but only if either the person who signed
// for this txn is the destination account or the source and dest are the same
if (account != destID && account_ != destID)
return tecNO_PERMISSION;
// create trustline
// RH UPTO
// 1. reduce sfLockedBalance on source line by escrow[sfAmount]
// 2. reduce sfBalance on source line by escrow[sfAmount]
// 3. increase sfBalance on dest line by escrow[sfAmount]
}
else
{
// trustline already exists
// check is it's frozen
if (destID != issuer)
{
if (sleLine->isFlag(destID > issuer ? lsfHighFreeze : lsfLowFreeze))
{
JLOG(ctx_.j.warn())
<< "Finishing an escrow for destination frozen trustline.";
return tecFROZEN;
}
}
// if balance is higher than limit then only allow if dest is signer
}
}
else
return tecINTERNAL; // should never happen
ctx_.view().update(sled); ctx_.view().update(sled);
// Adjust source owner count // Adjust source owner count

View File

@@ -333,6 +333,7 @@ extern uint256 const featureFlowSortStrands;
extern uint256 const fixSTAmountCanonicalize; extern uint256 const fixSTAmountCanonicalize;
extern uint256 const fixRmSmallIncreasedQOffers; extern uint256 const fixRmSmallIncreasedQOffers;
extern uint256 const featureCheckCashMakesTrustLine; extern uint256 const featureCheckCashMakesTrustLine;
extern uint256 const featurePaychanAndEscrowForTokens;
} // namespace ripple } // namespace ripple

View File

@@ -448,6 +448,7 @@ extern SF_AMOUNT const sfHighLimit;
extern SF_AMOUNT const sfFee; extern SF_AMOUNT const sfFee;
extern SF_AMOUNT const sfSendMax; extern SF_AMOUNT const sfSendMax;
extern SF_AMOUNT const sfDeliverMin; extern SF_AMOUNT const sfDeliverMin;
extern SF_AMOUNT const sfLockedBalance;
// currency amount (uncommon) // currency amount (uncommon)
extern SF_AMOUNT const sfMinimumOffer; extern SF_AMOUNT const sfMinimumOffer;

View File

@@ -516,6 +516,16 @@ isXRP(STAmount const& amount)
return isXRP(amount.issue().currency); return isXRP(amount.issue().currency);
} }
inline bool
isFakeXRP(STAmount const& amount)
{
if (mIsNative)
return false;
return isFakeXRP(amount.issue().currency);
}
// Since `canonicalize` does not have access to a ledger, this is needed to put // Since `canonicalize` does not have access to a ledger, this is needed to put
// the low-level routine stAmountCanonicalize on an amendment switch. Only // the low-level routine stAmountCanonicalize on an amendment switch. Only
// transactions need to use this switchover. Outside of a transaction it's safe // transactions need to use this switchover. Outside of a transaction it's safe

View File

@@ -77,6 +77,12 @@ isXRP(Currency const& c)
return c == beast::zero; return c == beast::zero;
} }
inline bool
isFakeXRP(Currency const& c)
{
return c == badCurrency();
}
/** Returns "", "XRP", or three letter ISO code. */ /** Returns "", "XRP", or three letter ISO code. */
std::string std::string
to_string(Currency const& c); to_string(Currency const& c);

View File

@@ -437,6 +437,7 @@ REGISTER_FEATURE(FlowSortStrands, Supported::yes, DefaultVote::yes
REGISTER_FIX (fixSTAmountCanonicalize, Supported::yes, DefaultVote::yes); REGISTER_FIX (fixSTAmountCanonicalize, Supported::yes, DefaultVote::yes);
REGISTER_FIX (fixRmSmallIncreasedQOffers, Supported::yes, DefaultVote::yes); REGISTER_FIX (fixRmSmallIncreasedQOffers, Supported::yes, DefaultVote::yes);
REGISTER_FEATURE(CheckCashMakesTrustLine, Supported::yes, DefaultVote::no); REGISTER_FEATURE(CheckCashMakesTrustLine, Supported::yes, DefaultVote::no);
REGISTER_FEATURE(PaychanAndEscrowForTokens, Supported::yes, DefaultVote::no);
// The following amendments have been active for at least two years. Their // The following amendments have been active for at least two years. Their
// pre-amendment code has been removed and the identifiers are deprecated. // pre-amendment code has been removed and the identifiers are deprecated.

View File

@@ -103,6 +103,7 @@ LedgerFormats::LedgerFormats()
{sfHighNode, soeOPTIONAL}, {sfHighNode, soeOPTIONAL},
{sfHighQualityIn, soeOPTIONAL}, {sfHighQualityIn, soeOPTIONAL},
{sfHighQualityOut, soeOPTIONAL}, {sfHighQualityOut, soeOPTIONAL},
{sfLockedBalance, soeOPTIONAL},
}, },
commonFields); commonFields);

View File

@@ -201,6 +201,8 @@ CONSTRUCT_TYPED_SFIELD(sfDeliverMin, "DeliverMin", AMOUNT,
CONSTRUCT_TYPED_SFIELD(sfMinimumOffer, "MinimumOffer", AMOUNT, 16); CONSTRUCT_TYPED_SFIELD(sfMinimumOffer, "MinimumOffer", AMOUNT, 16);
CONSTRUCT_TYPED_SFIELD(sfRippleEscrow, "RippleEscrow", AMOUNT, 17); CONSTRUCT_TYPED_SFIELD(sfRippleEscrow, "RippleEscrow", AMOUNT, 17);
CONSTRUCT_TYPED_SFIELD(sfDeliveredAmount, "DeliveredAmount", AMOUNT, 18); CONSTRUCT_TYPED_SFIELD(sfDeliveredAmount, "DeliveredAmount", AMOUNT, 18);
// 19, 20 left for xls20
CONSTRUCT_TYPED_SFIELD(sfLockedBalance, "LockedBalance", AMOUNT, 21);
// variable length (common) // variable length (common)
CONSTRUCT_TYPED_SFIELD(sfPublicKey, "PublicKey", VL, 1); CONSTRUCT_TYPED_SFIELD(sfPublicKey, "PublicKey", VL, 1);