mirror of
https://github.com/Xahau/xahaud.git
synced 2026-04-29 15:37:46 +00:00
add gateway feature
This commit is contained in:
@@ -118,13 +118,6 @@ EscrowCreate::preflight(PreflightContext const& ctx)
|
||||
|
||||
if (isFakeXRP(amount))
|
||||
return temBAD_CURRENCY;
|
||||
|
||||
if (ctx.tx[sfAccount] == amount.getIssuer())
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "Malformed transaction: Cannot escrow own tokens to self.";
|
||||
return temBAD_SRC_ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.tx[sfAmount] <= beast::zero)
|
||||
@@ -215,15 +208,15 @@ EscrowCreate::doApply()
|
||||
auto const account = ctx_.tx[sfAccount];
|
||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||
if (!sle)
|
||||
return temDISABLED;
|
||||
return tefINTERNAL;
|
||||
|
||||
STAmount const amount{ctx_.tx[sfAmount]};
|
||||
|
||||
std::shared_ptr<SLE> sleLine;
|
||||
|
||||
auto const balance = STAmount((*sle)[sfBalance]).xrp();
|
||||
auto const reserve =
|
||||
ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1);
|
||||
auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1);
|
||||
bool isIssuer = amount.getIssuer() == account;
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -244,37 +237,42 @@ EscrowCreate::doApply()
|
||||
|
||||
// check if the escrow is capable of being
|
||||
// finished before we allow it to be created
|
||||
if (!sleLine && amount.getIssuer() != account)
|
||||
return tecNO_LINE;
|
||||
|
||||
TER result = trustTransferAllowed(
|
||||
ctx_.view(),
|
||||
{account, ctx_.tx[sfDestination]},
|
||||
amount.issue(),
|
||||
ctx_.journal);
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCreate::doApply trustTransferAllowed result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
|
||||
// issuer does not need to lock anything
|
||||
if (!isIssuer)
|
||||
{
|
||||
TER result = trustTransferAllowed(
|
||||
ctx_.view(),
|
||||
{account, ctx_.tx[sfDestination]},
|
||||
amount.issue(),
|
||||
ctx_.journal);
|
||||
// perform the lock as a dry run before
|
||||
// we modify anything on-ledger
|
||||
sleLine = ctx_.view().peek(
|
||||
keylet::line(account, amount.getIssuer(), amount.getCurrency()));
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCreate::doApply trustTransferAllowed result="
|
||||
<< result;
|
||||
{
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun);
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCreate::doApply trustAdjustLockedBalance (dry) "
|
||||
"result="
|
||||
<< result;
|
||||
|
||||
// perform the lock as a dry run before
|
||||
// we modify anything on-ledger
|
||||
sleLine = ctx_.view().peek(
|
||||
keylet::line(account, amount.getIssuer(), amount.getCurrency()));
|
||||
|
||||
{
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun);
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCreate::doApply trustAdjustLockedBalance (dry) "
|
||||
"result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,20 +333,26 @@ EscrowCreate::doApply()
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
else
|
||||
{
|
||||
if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens) ||
|
||||
!sleLine)
|
||||
if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens))
|
||||
return temDISABLED;
|
||||
|
||||
// do the lock-up for real now
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun);
|
||||
if (!sleLine && amount.getIssuer() != account)
|
||||
return tecNO_LINE;
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCreate::doApply trustAdjustLockedBalance (wet) result="
|
||||
<< result;
|
||||
// issuer does not need to lock anything
|
||||
if (!isIssuer)
|
||||
{
|
||||
// do the lock-up for real now
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun);
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCreate::doApply trustAdjustLockedBalance (wet) result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
|
||||
@@ -682,6 +686,7 @@ EscrowCancel::doApply()
|
||||
AccountID const account = (*slep)[sfAccount];
|
||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||
auto amount = slep->getFieldAmount(sfAmount);
|
||||
bool isIssuer = amount.getIssuer() == account;
|
||||
|
||||
std::shared_ptr<SLE> sleLine;
|
||||
|
||||
@@ -690,14 +695,15 @@ EscrowCancel::doApply()
|
||||
if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens))
|
||||
return temDISABLED;
|
||||
|
||||
sleLine = ctx_.view().peek(
|
||||
keylet::line(account, amount.getIssuer(), amount.getCurrency()));
|
||||
// issuer does not need to lock anything
|
||||
if (!isIssuer)
|
||||
{
|
||||
sleLine = ctx_.view().peek(keylet::line(account, amount.getIssuer(), amount.getCurrency()));
|
||||
|
||||
// dry run before we make any changes to ledger
|
||||
if (TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, -amount, -1, ctx_.journal, DryRun);
|
||||
result != tesSUCCESS)
|
||||
return result;
|
||||
// dry run before we make any changes to ledger
|
||||
if (TER result = trustAdjustLockedBalance(ctx_.view(), sleLine, -amount, -1, ctx_.journal, DryRun); result != tesSUCCESS)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove escrow from owner directory
|
||||
@@ -733,16 +739,20 @@ EscrowCancel::doApply()
|
||||
if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens))
|
||||
return temDISABLED;
|
||||
|
||||
// unlock previously locked tokens from source line
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, -amount, -1, ctx_.journal, WetRun);
|
||||
// issuer does not need to lock anything
|
||||
if (!isIssuer)
|
||||
{
|
||||
// unlock previously locked tokens from source line
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, -amount, -1, ctx_.journal, WetRun);
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCancel::doApply trustAdjustLockedBalance (wet) result="
|
||||
<< result;
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "EscrowCancel::doApply trustAdjustLockedBalance (wet) result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement owner count
|
||||
|
||||
@@ -225,13 +225,6 @@ PayChanCreate::preflight(PreflightContext const& ctx)
|
||||
|
||||
if (isFakeXRP(amount))
|
||||
return temBAD_CURRENCY;
|
||||
|
||||
if (ctx.tx[sfAccount] == amount.getIssuer())
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "Malformed transaction: Cannot paychan own tokens to self.";
|
||||
return temBAD_SRC_ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.tx[sfAmount] <= beast::zero)
|
||||
@@ -264,6 +257,7 @@ PayChanCreate::preclaim(PreclaimContext const& ctx)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto const dst = ctx.tx[sfDestination];
|
||||
bool isIssuer = amount.getIssuer() == account;
|
||||
|
||||
// Check reserve and funds availability
|
||||
if (isXRP(amount) && balance < reserve + amount)
|
||||
@@ -287,20 +281,16 @@ PayChanCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
|
||||
// check if the amount can be locked
|
||||
{
|
||||
auto sleLine = ctx.view.read(keylet::line(
|
||||
account, amount.getIssuer(), amount.getCurrency()));
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx.view, sleLine, amount, 1, ctx.j, DryRun);
|
||||
|
||||
JLOG(ctx.j.trace()) << "PayChanCreate::preclaim "
|
||||
"trustAdjustLockedBalance(dry) result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
if (!isIssuer)
|
||||
{
|
||||
auto sleLine = ctx.view.read(keylet::line(account, amount.getIssuer(), amount.getCurrency()));
|
||||
TER result = trustAdjustLockedBalance(ctx.view, sleLine, amount, 1, ctx.j, DryRun);
|
||||
JLOG(ctx.j.trace()) << "PayChanCreate::preclaim trustAdjustLockedBalance(dry) result=" << result;
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,6 +324,7 @@ PayChanCreate::doApply()
|
||||
auto const dst = ctx_.tx[sfDestination];
|
||||
|
||||
STAmount const amount{ctx_.tx[sfAmount]};
|
||||
bool isIssuer = amount.getIssuer() == account;
|
||||
|
||||
// Create PayChan in ledger.
|
||||
//
|
||||
@@ -386,21 +377,22 @@ PayChanCreate::doApply()
|
||||
if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens))
|
||||
return temDISABLED;
|
||||
|
||||
auto sleLine = ctx_.view().peek(
|
||||
keylet::line(account, amount.getIssuer(), amount.getCurrency()));
|
||||
|
||||
if (!sleLine)
|
||||
auto sleLine = ctx_.view().peek(keylet::line(account, amount.getIssuer(), amount.getCurrency()));
|
||||
if (!sleLine && !isIssuer)
|
||||
return tecNO_LINE;
|
||||
|
||||
TER result = trustAdjustLockedBalance(
|
||||
|
||||
if (!isIssuer)
|
||||
{
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun);
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "PayChanCreate::doApply trustAdjustLockedBalance(wet) result="
|
||||
<< result;
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "PayChanCreate::doApply trustAdjustLockedBalance(wet) result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal);
|
||||
@@ -438,13 +430,6 @@ PayChanFund::preflight(PreflightContext const& ctx)
|
||||
|
||||
if (isFakeXRP(amount))
|
||||
return temBAD_CURRENCY;
|
||||
|
||||
if (ctx.tx[sfAccount] == amount.getIssuer())
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "Malformed transaction: Cannot escrow own tokens to self.";
|
||||
return temDST_IS_SRC;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.tx[sfAmount] <= beast::zero)
|
||||
@@ -466,27 +451,30 @@ PayChanFund::doApply()
|
||||
std::shared_ptr<SLE> sleLine; // if XRP or featurePaychanAndEscrowForTokens
|
||||
// not enabled this remains null
|
||||
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
auto const txAccount = ctx_.tx[sfAccount];
|
||||
auto const expiration = (*slep)[~sfExpiration];
|
||||
bool isIssuer = amount.getIssuer() == txAccount;
|
||||
// if this is a Fund operation on an IOU then perform a dry run here
|
||||
if (!isXRP(amount) &&
|
||||
ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens))
|
||||
{
|
||||
sleLine = ctx_.view().peek(keylet::line(
|
||||
(*slep)[sfAccount], amount.getIssuer(), amount.getCurrency()));
|
||||
// issuer does not need to lock anything
|
||||
if (!isIssuer)
|
||||
{
|
||||
sleLine = ctx_.view().peek(keylet::line((*slep)[sfAccount], amount.getIssuer(), amount.getCurrency()));
|
||||
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun);
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun);
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "PayChanFund::doApply trustAdjustLockedBalance(dry) result="
|
||||
<< result;
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "PayChanFund::doApply trustAdjustLockedBalance(dry) result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
auto const txAccount = ctx_.tx[sfAccount];
|
||||
auto const expiration = (*slep)[~sfExpiration];
|
||||
{
|
||||
auto const cancelAfter = (*slep)[~sfCancelAfter];
|
||||
auto const closeTime =
|
||||
@@ -547,15 +535,19 @@ PayChanFund::doApply()
|
||||
if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens))
|
||||
return temDISABLED;
|
||||
|
||||
TER result = trustAdjustLockedBalance(
|
||||
// issuer does not need to lock anything
|
||||
if (!isIssuer)
|
||||
{
|
||||
TER result = trustAdjustLockedBalance(
|
||||
ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun);
|
||||
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "PayChanFund::doApply trustAdjustLockedBalance(wet) result="
|
||||
<< result;
|
||||
JLOG(ctx_.journal.trace())
|
||||
<< "PayChanFund::doApply trustAdjustLockedBalance(wet) result="
|
||||
<< result;
|
||||
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
(*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount];
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
#ifndef RIPPLE_LEDGER_VIEW_H_INCLUDED
|
||||
#define RIPPLE_LEDGER_VIEW_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/ledger/ApplyView.h>
|
||||
#include <ripple/ledger/OpenView.h>
|
||||
#include <ripple/ledger/RawView.h>
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Protocol.h>
|
||||
#include <ripple/protocol/Rate.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
@@ -33,13 +35,11 @@
|
||||
#include <ripple/protocol/STTx.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
@@ -353,7 +353,6 @@ trustDelete(
|
||||
AccountID const& uHighAccountID,
|
||||
beast::Journal j);
|
||||
|
||||
|
||||
/** Delete an offer.
|
||||
|
||||
Requirements:
|
||||
@@ -422,7 +421,7 @@ transferXRP(
|
||||
// Trustline Locking and Transfer (PaychanAndEscrowForTokens)
|
||||
//
|
||||
|
||||
// In functions white require a `RunType`
|
||||
// In functions white require a `RunType`
|
||||
// pass DryRun (don't apply changes) or WetRun (do apply changes)
|
||||
// to allow compile time evaluation of which types and calls to use
|
||||
|
||||
@@ -435,15 +434,16 @@ transferXRP(
|
||||
template <class T, T V>
|
||||
struct RunType
|
||||
{
|
||||
//see:
|
||||
//http://alumni.media.mit.edu/~rahimi/compile-time-flags/
|
||||
constexpr operator T() const
|
||||
{
|
||||
// see:
|
||||
// http://alumni.media.mit.edu/~rahimi/compile-time-flags/
|
||||
constexpr operator T() const
|
||||
{
|
||||
static_assert(std::is_same<bool, T>::value);
|
||||
return V;
|
||||
}
|
||||
|
||||
constexpr T operator!() const
|
||||
constexpr T
|
||||
operator!() const
|
||||
{
|
||||
static_assert(std::is_same<bool, T>::value);
|
||||
return !(V);
|
||||
@@ -452,7 +452,8 @@ struct RunType
|
||||
|
||||
// allow party lists to be logged easily
|
||||
template <class T>
|
||||
std::ostream& operator<< (std::ostream& lhs, std::vector<T> const& rhs)
|
||||
std::ostream&
|
||||
operator<<(std::ostream& lhs, std::vector<T> const& rhs)
|
||||
{
|
||||
lhs << "{";
|
||||
for (int i = 0; i < rhs.size(); ++i)
|
||||
@@ -461,7 +462,8 @@ std::ostream& operator<< (std::ostream& lhs, std::vector<T> const& rhs)
|
||||
return lhs;
|
||||
}
|
||||
// Return true iff the acc side of line is in default state
|
||||
bool isTrustDefault(
|
||||
bool
|
||||
isTrustDefault(
|
||||
std::shared_ptr<SLE> const& acc, // side to check
|
||||
std::shared_ptr<SLE> const& line); // line to check
|
||||
|
||||
@@ -469,17 +471,17 @@ bool isTrustDefault(
|
||||
If positive deltaAmt lock the amount.
|
||||
If negative deltaAmt unlock the amount.
|
||||
*/
|
||||
template<class V, class S, class R>
|
||||
template <class V, class S, class R>
|
||||
[[nodiscard]] TER
|
||||
trustAdjustLockedBalance(
|
||||
V& view,
|
||||
S& sleLine,
|
||||
STAmount const& deltaAmt,
|
||||
int deltaLockCount, // if +1 lockCount is increased, -1 is decreased, 0 unchanged
|
||||
int deltaLockCount, // if +1 lockCount is increased, -1 is decreased, 0
|
||||
// unchanged
|
||||
beast::Journal const& j,
|
||||
R dryRun)
|
||||
{
|
||||
|
||||
static_assert(
|
||||
(std::is_same<V, ReadView const>::value &&
|
||||
std::is_same<S, std::shared_ptr<SLE const>>::value) ||
|
||||
@@ -498,27 +500,23 @@ trustAdjustLockedBalance(
|
||||
return tecNO_LINE;
|
||||
|
||||
// auto const currency = deltaAmt.getCurrency();
|
||||
auto const issuer = deltaAmt.getIssuer();
|
||||
auto const issuer = deltaAmt.getIssuer();
|
||||
|
||||
STAmount lowLimit = sleLine->getFieldAmount(sfLowLimit);
|
||||
STAmount lowLimit = sleLine->getFieldAmount(sfLowLimit);
|
||||
|
||||
// the account which is modifying the LockedBalance is always
|
||||
// the side that isn't the issuer, so if the low side is the
|
||||
// issuer then the high side is the account.
|
||||
bool high = lowLimit.getIssuer() == issuer;
|
||||
bool high = lowLimit.getIssuer() == issuer;
|
||||
|
||||
std::vector<AccountID> parties
|
||||
{high ? sleLine->getFieldAmount(sfHighLimit).getIssuer(): lowLimit.getIssuer()};
|
||||
std::vector<AccountID> parties{
|
||||
high ? sleLine->getFieldAmount(sfHighLimit).getIssuer()
|
||||
: lowLimit.getIssuer()};
|
||||
|
||||
// check for freezes & auth
|
||||
{
|
||||
TER result =
|
||||
trustTransferAllowed(
|
||||
view,
|
||||
parties,
|
||||
deltaAmt.issue(),
|
||||
j);
|
||||
|
||||
TER result = trustTransferAllowed(view, parties, deltaAmt.issue(), j);
|
||||
|
||||
JLOG(j.trace())
|
||||
<< "trustAdjustLockedBalance: trustTransferAllowed result="
|
||||
<< result;
|
||||
@@ -528,8 +526,7 @@ trustAdjustLockedBalance(
|
||||
}
|
||||
|
||||
// pull the TL balance from the account's perspective
|
||||
STAmount balance =
|
||||
high ? -(*sleLine)[sfBalance] : (*sleLine)[sfBalance];
|
||||
STAmount balance = high ? -(*sleLine)[sfBalance] : (*sleLine)[sfBalance];
|
||||
|
||||
// this would mean somehow the issuer is trying to lock balance
|
||||
if (balance < beast::zero)
|
||||
@@ -541,14 +538,13 @@ trustAdjustLockedBalance(
|
||||
// can't lock or unlock a zero balance
|
||||
if (balance == beast::zero)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustAdjustLockedBalance failed, zero balance";
|
||||
JLOG(j.trace()) << "trustAdjustLockedBalance failed, zero balance";
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
|
||||
STAmount priorLockedBalance {sfLockedBalance, deltaAmt.issue()};
|
||||
STAmount priorLockedBalance{sfLockedBalance, deltaAmt.issue()};
|
||||
if (sleLine->isFieldPresent(sfLockedBalance))
|
||||
priorLockedBalance =
|
||||
priorLockedBalance =
|
||||
high ? -(*sleLine)[sfLockedBalance] : (*sleLine)[sfLockedBalance];
|
||||
|
||||
uint32_t priorLockCount = 0;
|
||||
@@ -560,19 +556,15 @@ trustAdjustLockedBalance(
|
||||
|
||||
if (finalLockedBalance > balance)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustAdjustLockedBalance: "
|
||||
<< "lockedBalance("
|
||||
<< finalLockedBalance
|
||||
<< ") > balance("
|
||||
<< balance
|
||||
<< ") = true\n";
|
||||
JLOG(j.trace()) << "trustAdjustLockedBalance: "
|
||||
<< "lockedBalance(" << finalLockedBalance
|
||||
<< ") > balance(" << balance << ") = true\n";
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
|
||||
if (finalLockedBalance < beast::zero)
|
||||
return tecINTERNAL;
|
||||
|
||||
|
||||
// check if there is significant precision loss
|
||||
if (!isAddable(balance, deltaAmt) ||
|
||||
!isAddable(priorLockedBalance, deltaAmt) ||
|
||||
@@ -589,7 +581,9 @@ trustAdjustLockedBalance(
|
||||
if (dryRun)
|
||||
return tesSUCCESS;
|
||||
|
||||
if constexpr(std::is_same<V, ApplyView>::value && std::is_same<S, std::shared_ptr<SLE>>::value)
|
||||
if constexpr (
|
||||
std::is_same<V, ApplyView>::value &&
|
||||
std::is_same<S, std::shared_ptr<SLE>>::value)
|
||||
{
|
||||
if (finalLockedBalance == beast::zero || finalLockCount == 0)
|
||||
{
|
||||
@@ -598,18 +592,18 @@ trustAdjustLockedBalance(
|
||||
}
|
||||
else
|
||||
{
|
||||
sleLine->
|
||||
setFieldAmount(sfLockedBalance, high ? -finalLockedBalance : finalLockedBalance);
|
||||
sleLine->setFieldAmount(
|
||||
sfLockedBalance,
|
||||
high ? -finalLockedBalance : finalLockedBalance);
|
||||
sleLine->setFieldU32(sfLockCount, finalLockCount);
|
||||
}
|
||||
|
||||
|
||||
view.update(sleLine);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Check if a set of accounts can freely exchange the specified token.
|
||||
Read only, does not change any ledger object.
|
||||
May be called with ApplyView or ReadView.
|
||||
@@ -618,8 +612,8 @@ trustAdjustLockedBalance(
|
||||
If parties contains more than 1 entry then any party with noRipple
|
||||
on issuer side is a bar to xfer.
|
||||
*/
|
||||
template<class V>
|
||||
[[nodiscard]]TER
|
||||
template <class V>
|
||||
[[nodiscard]] TER
|
||||
trustTransferAllowed(
|
||||
V& view,
|
||||
std::vector<AccountID> const& parties,
|
||||
@@ -629,13 +623,12 @@ trustTransferAllowed(
|
||||
static_assert(
|
||||
std::is_same<V, ReadView const>::value ||
|
||||
std::is_same<V, ApplyView>::value);
|
||||
|
||||
|
||||
typedef typename std::conditional<
|
||||
std::is_same<V, ApplyView>::value,
|
||||
std::shared_ptr<SLE>,
|
||||
std::shared_ptr<SLE const>>::type SLEPtr;
|
||||
|
||||
|
||||
if (isFakeXRP(issue.currency))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
@@ -656,12 +649,13 @@ trustTransferAllowed(
|
||||
|
||||
bool requireAuth = issuerFlags & lsfRequireAuth;
|
||||
|
||||
for (AccountID const& p: parties)
|
||||
for (AccountID const& p : parties)
|
||||
{
|
||||
if (p == issue.account)
|
||||
continue;
|
||||
|
||||
auto const line = view.read(keylet::line(p, issue.account, issue.currency));
|
||||
auto const line =
|
||||
view.read(keylet::line(p, issue.account, issue.currency));
|
||||
if (!line)
|
||||
{
|
||||
if (requireAuth)
|
||||
@@ -686,16 +680,16 @@ trustTransferAllowed(
|
||||
// these "strange" old lines, if they even exist anymore are
|
||||
// always a bar to xfer
|
||||
if (line->getFieldAmount(sfLowLimit).getIssuer() ==
|
||||
line->getFieldAmount(sfHighLimit).getIssuer())
|
||||
line->getFieldAmount(sfHighLimit).getIssuer())
|
||||
return tecINTERNAL;
|
||||
|
||||
if (line->isFieldPresent(sfLockedBalance))
|
||||
{
|
||||
if (!lockedBalanceAllowed)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "trustTransferAllowed: "
|
||||
<< "sfLockedBalance found on line when amendment not enabled";
|
||||
JLOG(j.warn()) << "trustTransferAllowed: "
|
||||
<< "sfLockedBalance found on line when "
|
||||
"amendment not enabled";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
@@ -704,9 +698,9 @@ trustTransferAllowed(
|
||||
|
||||
if (lockedBalance.getCurrency() != balance.getCurrency())
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "trustTansferAllowed: "
|
||||
<< "lockedBalance currency did not match balance currency";
|
||||
JLOG(j.warn()) << "trustTansferAllowed: "
|
||||
<< "lockedBalance currency did not match "
|
||||
"balance currency";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
}
|
||||
@@ -719,19 +713,19 @@ trustTransferAllowed(
|
||||
{
|
||||
bool pHigh = p > issue.account;
|
||||
|
||||
auto const flagIssuerNoRipple { pHigh ? lsfLowNoRipple : lsfHighNoRipple };
|
||||
auto const flagIssuerFreeze { pHigh ? lsfLowFreeze : lsfHighFreeze };
|
||||
auto const flagIssuerAuth { pHigh ? lsfLowAuth : lsfHighAuth };
|
||||
auto const flagIssuerNoRipple{
|
||||
pHigh ? lsfLowNoRipple : lsfHighNoRipple};
|
||||
auto const flagIssuerFreeze{pHigh ? lsfLowFreeze : lsfHighFreeze};
|
||||
auto const flagIssuerAuth{pHigh ? lsfLowAuth : lsfHighAuth};
|
||||
|
||||
uint32_t flags = line->getFieldU32(sfFlags);
|
||||
|
||||
if (flags & flagIssuerFreeze)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferAllowed: "
|
||||
<< "parties=[" << parties << "], "
|
||||
<< "issuer: " << issue.account << " "
|
||||
<< "has freeze on party: " << p;
|
||||
JLOG(j.trace()) << "trustTransferAllowed: "
|
||||
<< "parties=[" << parties << "], "
|
||||
<< "issuer: " << issue.account << " "
|
||||
<< "has freeze on party: " << p;
|
||||
return tecFROZEN;
|
||||
}
|
||||
|
||||
@@ -740,11 +734,10 @@ trustTransferAllowed(
|
||||
// blocks any possible xfer
|
||||
if (parties.size() > 1 && (flags & flagIssuerNoRipple))
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferAllowed: "
|
||||
<< "parties=[" << parties << "], "
|
||||
<< "issuer: " << issue.account << " "
|
||||
<< "has noRipple on party: " << p;
|
||||
JLOG(j.trace()) << "trustTransferAllowed: "
|
||||
<< "parties=[" << parties << "], "
|
||||
<< "issuer: " << issue.account << " "
|
||||
<< "has noRipple on party: " << p;
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
@@ -752,13 +745,12 @@ trustTransferAllowed(
|
||||
// the issuer has specified lsfRequireAuth
|
||||
if (requireAuth && !(flags & flagIssuerAuth))
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferAllowed: "
|
||||
<< "parties=[" << parties << "], "
|
||||
<< "issuer: " << issue.account << " "
|
||||
<< "requires TL auth which "
|
||||
<< "party: " << p << " "
|
||||
<< "does not possess.";
|
||||
JLOG(j.trace()) << "trustTransferAllowed: "
|
||||
<< "parties=[" << parties << "], "
|
||||
<< "issuer: " << issue.account << " "
|
||||
<< "requires TL auth which "
|
||||
<< "party: " << p << " "
|
||||
<< "does not possess.";
|
||||
return tecNO_AUTH;
|
||||
}
|
||||
}
|
||||
@@ -775,22 +767,20 @@ template <class V, class S, class R>
|
||||
[[nodiscard]] TER
|
||||
trustTransferLockedBalance(
|
||||
V& view,
|
||||
AccountID const& actingAccID, // the account whose tx is actioning xfer
|
||||
AccountID const& actingAccID, // the account whose tx is actioning xfer
|
||||
S& sleSrcAcc,
|
||||
S& sleDstAcc,
|
||||
STAmount const& amount, // issuer, currency are in this field
|
||||
int deltaLockCount, // -1 decrement, +1 increment, 0 unchanged
|
||||
STAmount const& amount, // issuer, currency are in this field
|
||||
int deltaLockCount, // -1 decrement, +1 increment, 0 unchanged
|
||||
beast::Journal const& j,
|
||||
R dryRun)
|
||||
{
|
||||
|
||||
typedef typename std::conditional<
|
||||
std::is_same<V, ApplyView>::value && !dryRun,
|
||||
std::shared_ptr<SLE>,
|
||||
std::shared_ptr<SLE const>>::type SLEPtr;
|
||||
|
||||
auto peek = [&](Keylet& k)
|
||||
{
|
||||
auto peek = [&](Keylet& k) {
|
||||
if constexpr (std::is_same<V, ApplyView>::value && !dryRun)
|
||||
return const_cast<ApplyView&>(view).peek(k);
|
||||
else
|
||||
@@ -804,15 +794,13 @@ trustTransferLockedBalance(
|
||||
|
||||
if (!sleSrcAcc || !sleDstAcc)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "trustTransferLockedBalance without sleSrc/sleDst";
|
||||
JLOG(j.warn()) << "trustTransferLockedBalance without sleSrc/sleDst";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
if (amount <= beast::zero)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "trustTransferLockedBalance with non-positive amount";
|
||||
JLOG(j.warn()) << "trustTransferLockedBalance with non-positive amount";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
@@ -823,15 +811,12 @@ trustTransferLockedBalance(
|
||||
|
||||
bool srcHigh = srcAccID > issuerAccID;
|
||||
bool dstHigh = dstAccID > issuerAccID;
|
||||
bool isIssuer = issuerAccID == srcAccID;
|
||||
|
||||
// check for freezing, auth, no ripple and TL sanity
|
||||
{
|
||||
TER result =
|
||||
trustTransferAllowed(
|
||||
view,
|
||||
{srcAccID, dstAccID},
|
||||
{currency, issuerAccID},
|
||||
j);
|
||||
TER result = trustTransferAllowed(
|
||||
view, {srcAccID, dstAccID}, {currency, issuerAccID}, j);
|
||||
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferLockedBalance: trustTransferAlowed result="
|
||||
@@ -841,85 +826,94 @@ trustTransferLockedBalance(
|
||||
}
|
||||
|
||||
// ensure source line exists
|
||||
Keylet klSrcLine { keylet::line(srcAccID, issuerAccID, currency)};
|
||||
Keylet klSrcLine{keylet::line(srcAccID, issuerAccID, currency)};
|
||||
SLEPtr sleSrcLine = peek(klSrcLine);
|
||||
|
||||
if (!sleSrcLine)
|
||||
return tecNO_LINE;
|
||||
|
||||
// can't transfer a locked balance that does not exist
|
||||
if (!sleSrcLine->isFieldPresent(sfLockedBalance) || !sleSrcLine->isFieldPresent(sfLockCount))
|
||||
// source account IS issuer
|
||||
if (!isIssuer)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferLockedBalance could not find sfLockedBalance/sfLockCount on source line";
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
if (!sleSrcLine)
|
||||
return tecNO_LINE;
|
||||
|
||||
|
||||
// decrement source balance
|
||||
{
|
||||
STAmount priorBalance =
|
||||
srcHigh ? -((*sleSrcLine)[sfBalance]) : (*sleSrcLine)[sfBalance];
|
||||
|
||||
STAmount priorLockedBalance =
|
||||
srcHigh ? -((*sleSrcLine)[sfLockedBalance]) : (*sleSrcLine)[sfLockedBalance];
|
||||
|
||||
uint32_t priorLockCount = (*sleSrcLine)[sfLockCount];
|
||||
|
||||
AccountID srcIssuerAccID =
|
||||
sleSrcLine->getFieldAmount(srcHigh ? sfLowLimit : sfHighLimit).getIssuer();
|
||||
|
||||
// check they have sufficient funds
|
||||
if (amount > priorLockedBalance)
|
||||
// can't transfer a locked balance that does not exist
|
||||
if (!sleSrcLine->isFieldPresent(sfLockedBalance) ||
|
||||
!sleSrcLine->isFieldPresent(sfLockCount))
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferLockedBalance amount > lockedBalance: "
|
||||
<< "amount=" << amount << " lockedBalance="
|
||||
<< priorLockedBalance;
|
||||
JLOG(j.trace()) << "trustTransferLockedBalance could not find "
|
||||
"sfLockedBalance/sfLockCount on source line";
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
|
||||
STAmount finalBalance = priorBalance - amount;
|
||||
|
||||
STAmount finalLockedBalance = priorLockedBalance - amount;
|
||||
|
||||
uint32_t finalLockCount = priorLockCount + deltaLockCount;
|
||||
|
||||
// check if there is significant precision loss
|
||||
if (!isAddable(priorBalance, amount) ||
|
||||
!isAddable(priorLockedBalance, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
// sanity check possible overflows on the lock counter
|
||||
if ((deltaLockCount > 0 && priorLockCount > finalLockCount) ||
|
||||
(deltaLockCount < 0 && priorLockCount < finalLockCount) ||
|
||||
(deltaLockCount == 0 && priorLockCount != finalLockCount))
|
||||
return tecOVERSIZE;
|
||||
|
||||
// this should never happen but defensively check it here before updating sle
|
||||
if (finalBalance < beast::zero || finalLockedBalance < beast::zero)
|
||||
// decrement source balance
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "trustTransferLockedBalance results in a negative balance on source line";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
STAmount priorBalance = srcHigh ? -((*sleSrcLine)[sfBalance])
|
||||
: (*sleSrcLine)[sfBalance];
|
||||
|
||||
if constexpr(!dryRun)
|
||||
{
|
||||
sleSrcLine->setFieldAmount(sfBalance, srcHigh ? -finalBalance : finalBalance);
|
||||
STAmount priorLockedBalance = srcHigh
|
||||
? -((*sleSrcLine)[sfLockedBalance])
|
||||
: (*sleSrcLine)[sfLockedBalance];
|
||||
|
||||
if (finalLockedBalance == beast::zero || finalLockCount == 0)
|
||||
uint32_t priorLockCount = (*sleSrcLine)[sfLockCount];
|
||||
|
||||
AccountID srcIssuerAccID =
|
||||
sleSrcLine->getFieldAmount(srcHigh ? sfLowLimit : sfHighLimit)
|
||||
.getIssuer();
|
||||
|
||||
// check they have sufficient funds
|
||||
if (amount > priorLockedBalance)
|
||||
{
|
||||
sleSrcLine->makeFieldAbsent(sfLockedBalance);
|
||||
sleSrcLine->makeFieldAbsent(sfLockCount);
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferLockedBalance amount > lockedBalance: "
|
||||
<< "amount=" << amount
|
||||
<< " lockedBalance=" << priorLockedBalance;
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
else
|
||||
|
||||
STAmount finalBalance = priorBalance - amount;
|
||||
|
||||
STAmount finalLockedBalance = priorLockedBalance - amount;
|
||||
|
||||
uint32_t finalLockCount = priorLockCount + deltaLockCount;
|
||||
|
||||
// check if there is significant precision loss
|
||||
if (!isAddable(priorBalance, amount) ||
|
||||
!isAddable(priorLockedBalance, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
// sanity check possible overflows on the lock counter
|
||||
if ((deltaLockCount > 0 && priorLockCount > finalLockCount) ||
|
||||
(deltaLockCount < 0 && priorLockCount < finalLockCount) ||
|
||||
(deltaLockCount == 0 && priorLockCount != finalLockCount))
|
||||
return tecOVERSIZE;
|
||||
|
||||
// this should never happen but defensively check it here before
|
||||
// updating sle
|
||||
if (finalBalance < beast::zero || finalLockedBalance < beast::zero)
|
||||
{
|
||||
sleSrcLine->setFieldAmount(sfLockedBalance, srcHigh ? -finalLockedBalance : finalLockedBalance);
|
||||
sleSrcLine->setFieldU32(sfLockCount, finalLockCount);
|
||||
JLOG(j.warn()) << "trustTransferLockedBalance results in a "
|
||||
"negative balance on source line";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
if constexpr (!dryRun)
|
||||
{
|
||||
sleSrcLine->setFieldAmount(
|
||||
sfBalance, srcHigh ? -finalBalance : finalBalance);
|
||||
|
||||
if (finalLockedBalance == beast::zero || finalLockCount == 0)
|
||||
{
|
||||
sleSrcLine->makeFieldAbsent(sfLockedBalance);
|
||||
sleSrcLine->makeFieldAbsent(sfLockCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
sleSrcLine->setFieldAmount(
|
||||
sfLockedBalance,
|
||||
srcHigh ? -finalLockedBalance : finalLockedBalance);
|
||||
sleSrcLine->setFieldU32(sfLockCount, finalLockCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// dstLow XNOR srcLow tells us if we need to flip the balance amount
|
||||
@@ -931,10 +925,9 @@ trustTransferLockedBalance(
|
||||
|
||||
// the destination will sometimes get less depending on xfer rate
|
||||
// with any difference in tokens burned
|
||||
auto dstAmt =
|
||||
xferRate == parityRate
|
||||
? amount
|
||||
: multiplyRound(amount, xferRate, amount.issue(), true);
|
||||
auto dstAmt = xferRate == parityRate
|
||||
? amount
|
||||
: multiplyRound(amount, xferRate, amount.issue(), true);
|
||||
|
||||
// check for a destination line
|
||||
Keylet klDstLine = keylet::line(dstAccID, issuerAccID, currency);
|
||||
@@ -953,9 +946,12 @@ trustTransferLockedBalance(
|
||||
dstBalanceDrops < view.fees().accountReserve(ownerCount + 1))
|
||||
return tecNO_LINE_INSUF_RESERVE;
|
||||
|
||||
// yes we can... we will
|
||||
// yes we can... we will
|
||||
|
||||
if constexpr(!dryRun)
|
||||
auto const finalDstAmt = isIssuer ? dstAmt
|
||||
: flipDstAmt ? -dstAmt
|
||||
: dstAmt;
|
||||
if constexpr (!dryRun)
|
||||
{
|
||||
// clang-format off
|
||||
if (TER const ter = trustCreate(
|
||||
@@ -968,7 +964,7 @@ trustTransferLockedBalance(
|
||||
false, // authorize account
|
||||
(sleDstAcc->getFlags() & lsfDefaultRipple) == 0,
|
||||
false, // freeze trust line
|
||||
flipDstAmt ? -dstAmt : dstAmt, // initial balance
|
||||
finalDstAmt, // initial balance
|
||||
Issue(currency, dstAccID), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
@@ -996,45 +992,52 @@ trustTransferLockedBalance(
|
||||
|
||||
if (finalBalance < priorBalance)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "trustTransferLockedBalance resulted in a lower/equal final balance on dest line";
|
||||
JLOG(j.warn()) << "trustTransferLockedBalance resulted in a "
|
||||
"lower/equal final balance on dest line";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
if (finalBalance > dstLimit && actingAccID != dstAccID)
|
||||
{
|
||||
JLOG(j.trace())
|
||||
<< "trustTransferLockedBalance would increase dest line above limit without permission";
|
||||
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;
|
||||
|
||||
if constexpr(!dryRun)
|
||||
sleDstLine->setFieldAmount(sfBalance, dstHigh ? -finalBalance : finalBalance);
|
||||
finalBalance = isIssuer ? -finalBalance
|
||||
: dstHigh
|
||||
? -finalBalance
|
||||
: finalBalance;
|
||||
if constexpr (!dryRun)
|
||||
sleDstLine->setFieldAmount(sfBalance, finalBalance);
|
||||
}
|
||||
|
||||
if constexpr (!dryRun)
|
||||
{
|
||||
static_assert(std::is_same<V, ApplyView>::value);
|
||||
|
||||
// check if source line ended up in default state and adjust owner count if it did
|
||||
if (isTrustDefault(sleSrcAcc, sleSrcLine))
|
||||
// check if source line ended up in default state and adjust owner count
|
||||
// if it did
|
||||
if (!isIssuer)
|
||||
{
|
||||
uint32_t flags = sleSrcLine->getFieldU32(sfFlags);
|
||||
uint32_t fReserve { srcHigh ? lsfHighReserve : lsfLowReserve };
|
||||
if (flags & fReserve)
|
||||
if (isTrustDefault(sleSrcAcc, sleSrcLine))
|
||||
{
|
||||
sleSrcLine->setFieldU32(sfFlags, flags & ~fReserve);
|
||||
adjustOwnerCount(view, sleSrcAcc, -1, j);
|
||||
view.update(sleSrcAcc);
|
||||
uint32_t flags = sleSrcLine->getFieldU32(sfFlags);
|
||||
uint32_t fReserve{srcHigh ? lsfHighReserve : lsfLowReserve};
|
||||
if (flags & fReserve)
|
||||
{
|
||||
sleSrcLine->setFieldU32(sfFlags, flags & ~fReserve);
|
||||
adjustOwnerCount(view, sleSrcAcc, -1, j);
|
||||
view.update(sleSrcAcc);
|
||||
}
|
||||
}
|
||||
view.update(sleSrcLine);
|
||||
}
|
||||
|
||||
view.update(sleSrcLine);
|
||||
|
||||
// a destination line already existed and was updated
|
||||
if (sleDstLine)
|
||||
view.update(sleDstLine);
|
||||
|
||||
@@ -3483,6 +3483,53 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testICGW(FeatureBitset features)
|
||||
{
|
||||
testcase("IC Gateway");
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const USD = gw["USD"];
|
||||
{
|
||||
// test create escrow from issuer with ic
|
||||
// test with dest tl
|
||||
// test finish from destination account
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(5000), alice, gw);
|
||||
env.close();
|
||||
env.trust(USD(10000), alice);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(5000)));
|
||||
env.close();
|
||||
auto const seq1 = env.seq(gw);
|
||||
auto const preAlice = env.balance(alice, USD.issue());
|
||||
env(escrow(gw, alice, USD(1000)), condition(cb1), finish_time(env.now() + 1s), fee(1500));
|
||||
env.close();
|
||||
env(finish(alice, gw, seq1), condition(cb1), fulfillment(fb1), fee(1500));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + USD(1000));
|
||||
}
|
||||
{
|
||||
// setup env
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(5000), alice, gw);
|
||||
env.close();
|
||||
|
||||
// test create escrow from issuer with ic no dest tl
|
||||
auto const seq1 = env.seq(gw);
|
||||
auto const preAlice = env.balance(alice, USD.issue());
|
||||
env(escrow(gw, alice, USD(1000)), condition(cb1), finish_time(env.now() + 1s), fee(1500));
|
||||
env.close();
|
||||
// test finish from dest account
|
||||
env(finish(alice, gw, seq1), condition(cb1), fulfillment(fb1), fee(1500));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + USD(1000));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testWithFeats(FeatureBitset features)
|
||||
{
|
||||
@@ -3508,6 +3555,7 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
testICMetaAndOwnership(features);
|
||||
testICConsequences(features);
|
||||
testICEscrowWithTickets(features);
|
||||
testICGW(features);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -122,6 +122,17 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
return STAmount(iou, 0);
|
||||
}
|
||||
|
||||
static STAmount
|
||||
limitAmount(
|
||||
jtx::Env const& env,
|
||||
jtx::Account const& account,
|
||||
jtx::Account const& gw,
|
||||
jtx::IOU const& iou)
|
||||
{
|
||||
auto const sle = env.le(keylet::line(account, gw, iou.currency));
|
||||
return (*sle)[sfHighLimit];
|
||||
}
|
||||
|
||||
static std::optional<std::int64_t>
|
||||
channelExpiration(ReadView const& view, uint256 const& chan)
|
||||
{
|
||||
@@ -4540,9 +4551,130 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testICAbuseTL(FeatureBitset features)
|
||||
testICGateway(FeatureBitset features)
|
||||
{
|
||||
testcase("IC Abuse Trust Line");
|
||||
testcase("IC Gateway");
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const USD = gw["USD"];
|
||||
{
|
||||
// test create paychan from issuer with ic
|
||||
// test where dest has no tl
|
||||
// test claim from issuer account
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, gw);
|
||||
env.close();
|
||||
auto const pk = gw.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(gw, alice, env.seq(gw));
|
||||
env(create(gw, alice, USD(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
// gw can not claim
|
||||
auto const preAlice = env.balance(alice, USD.issue());
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const delta = USD(500);
|
||||
auto const reqBal = chanBal + delta;
|
||||
auto const authAmt = reqBal + USD(100);
|
||||
env(claim(gw, chan, reqBal, authAmt), ter(tecNO_LINE));
|
||||
}
|
||||
{
|
||||
// test create paychan from issuer with ic
|
||||
// test where dest has no tl
|
||||
// test claim from destination account
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, gw);
|
||||
env.close();
|
||||
auto const pk = gw.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(gw, alice, env.seq(gw));
|
||||
env(create(gw, alice, USD(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
// alice can claim
|
||||
auto const preAlice = env.balance(alice, USD.issue());
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const delta = USD(500);
|
||||
auto const reqBal = chanBal + delta;
|
||||
auto const authAmt = reqBal + USD(100);
|
||||
auto const sig = signClaimICAuth(gw.pk(), gw.sk(), chan, authAmt);
|
||||
env(claim(alice, chan, reqBal, authAmt, Slice(sig), gw.pk()));
|
||||
env.close();
|
||||
BEAST_EXPECT(preAlice == USD(0));
|
||||
BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta);
|
||||
}
|
||||
{
|
||||
// test create paychan from issuer with ic
|
||||
// test where dest has tl
|
||||
// test claim from issuer/source account
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, gw);
|
||||
env.close();
|
||||
env.trust(USD(100000), alice);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env.close();
|
||||
auto const pk = gw.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(gw, alice, env.seq(gw));
|
||||
env(create(gw, alice, USD(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
// gw can claim
|
||||
auto const preAlice = env.balance(alice, USD.issue());
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const delta = USD(500);
|
||||
auto const reqBal = chanBal + delta;
|
||||
auto const authAmt = reqBal + USD(100);
|
||||
env(claim(gw, chan, reqBal, authAmt));
|
||||
env.close();
|
||||
BEAST_EXPECT(preAlice == USD(10000));
|
||||
BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta);
|
||||
}
|
||||
{
|
||||
// test create paychan from issuer with ic
|
||||
// test where dest has tl
|
||||
// test fund paychan from issuer with ic
|
||||
// test claim from issuer/source account
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, gw);
|
||||
env.close();
|
||||
env.trust(USD(100000), alice);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env.close();
|
||||
auto const pk = gw.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(gw, alice, env.seq(gw));
|
||||
env(create(gw, alice, USD(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
env(fund(gw, chan, USD(1000)));
|
||||
|
||||
// gw can claim
|
||||
auto const preAlice = env.balance(alice, USD.issue());
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const delta = USD(500);
|
||||
auto const reqBal = chanBal + delta;
|
||||
auto const authAmt = reqBal + USD(100);
|
||||
env(claim(gw, chan, reqBal, authAmt));
|
||||
env.close();
|
||||
BEAST_EXPECT(preAlice == USD(10000));
|
||||
BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testICTLFeatures(FeatureBitset features)
|
||||
{
|
||||
testcase("IC Trustline Features");
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
@@ -4550,8 +4682,213 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
auto const bob = Account("bob");
|
||||
auto const carol = Account("carol");
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const USD = gw["USD"];
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
auto const aliceUSD = alice["USD"];
|
||||
auto const bobUSD = bob["USD"];
|
||||
|
||||
// test LimitAmount
|
||||
{
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env.trust(USD(1000), alice);
|
||||
env.trust(USD(1000), bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(1000)));
|
||||
env(pay(gw, bob, USD(1000)));
|
||||
env.close();
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, USD(1000), settleDelay, pk));
|
||||
env.close();
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == USD(0));
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == USD(1000));
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
BEAST_EXPECT(chanBal == USD(0));
|
||||
BEAST_EXPECT(chanAmt == USD(1000));
|
||||
auto preBob = env.balance(bob);
|
||||
auto const delta = USD(50);
|
||||
auto reqBal = chanBal + delta;
|
||||
auto authAmt = reqBal + USD(100);
|
||||
assert(reqBal <= chanAmt);
|
||||
auto const preLocked = lockedAmount(env, alice, gw, USD);
|
||||
BEAST_EXPECT(preLocked == USD(1000));
|
||||
// alice cannot claim because bobs amount would be > than limit
|
||||
env(claim(alice, chan, reqBal, authAmt), ter(tecPATH_DRY));
|
||||
|
||||
// bob can claim, increasing the limit amount
|
||||
auto const preBobLimit = limitAmount(env, bob, gw, USD);
|
||||
auto const sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt);
|
||||
env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
|
||||
env.close();
|
||||
auto const postBobLimit = limitAmount(env, bob, gw, USD);
|
||||
// BEAST_EXPECT(postBobLimit == preBobLimit + delta);
|
||||
}
|
||||
// test asfRequireAuth
|
||||
{
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(1000), alice, bob, gw);
|
||||
env(fset(gw, asfRequireAuth));
|
||||
env.close();
|
||||
env(trust(gw, aliceUSD(10000)), txflags(tfSetfAuth));
|
||||
env(trust(alice, USD(10000)));
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(1000)));
|
||||
env.close();
|
||||
|
||||
auto const gwLimit = limitAmount(env, gw, alice, aliceUSD);
|
||||
auto const aliceLimit = limitAmount(env, alice, gw, USD);
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto chan = channel(alice, bob, env.seq(alice));
|
||||
// alice cannot create because bob's trustline is not authorized
|
||||
// all parties must be authorized
|
||||
env(create(alice, bob, USD(100), settleDelay, pk), ter(tecNO_AUTH));
|
||||
env.close();
|
||||
|
||||
env(trust(gw, bobUSD(10000)), txflags(tfSetfAuth));
|
||||
env(trust(bob, USD(10000)));
|
||||
env.close();
|
||||
env(pay(gw, bob, USD(1000)));
|
||||
env.close();
|
||||
|
||||
// alice can now create because bob's trustline is authorized
|
||||
chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, USD(100), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
auto const delta = USD(50);
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
auto reqBal = chanBal + delta;
|
||||
auto authAmt = reqBal + USD(100);
|
||||
// alice can claim
|
||||
env(claim(alice, chan, reqBal, authAmt));
|
||||
env.close();
|
||||
// bob can claim
|
||||
// auto const sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt);
|
||||
// env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
|
||||
// env.close();
|
||||
}
|
||||
// test TransferRate
|
||||
{
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
env.trust(USD(100000), alice);
|
||||
env.trust(USD(100000), bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env(pay(gw, bob, USD(10000)));
|
||||
env.close();
|
||||
|
||||
// env(pay(alice, bob, USD(100)), sendmax(USD(125)));
|
||||
// env(pay(alice, bob, USD(100)), txflags(tfPartialPayment));
|
||||
// env.close();
|
||||
// env.require(
|
||||
// balance(alice, xrpMinusFee(env, 10000 - 50)),
|
||||
// balance(bob, USD(2.5)), // owner pays transfer fee
|
||||
// balance(carol, USD(50)));
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, USD(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const delta = USD(100);
|
||||
auto reqBal = chanBal + delta;
|
||||
auto authAmt = reqBal + USD(200);
|
||||
// alice can claim
|
||||
env(claim(alice, chan, reqBal, authAmt));
|
||||
env.close();
|
||||
|
||||
// bob can claim, increasing the limit amount
|
||||
// auto const preBobLimit = limitAmount(env, bob, gw, USD);
|
||||
// auto const sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt);
|
||||
// env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
|
||||
// env.close();
|
||||
|
||||
auto const postLocked = lockedAmount(env, alice, gw, USD);
|
||||
auto const aliceLimit = limitAmount(env, alice, gw, USD);
|
||||
auto const bobLimit = limitAmount(env, bob, gw, USD);
|
||||
// std::cout << "ALICE AMOUNT: " << env.balance(alice, USD.issue()) << "\n";
|
||||
// std::cout << "BOB AMOUNT: " << env.balance(bob, USD.issue()) << "\n";
|
||||
// std::cout << "ALICE LIMIT: " << aliceLimit << "\n";
|
||||
// std::cout << "BOB LIMIT: " << bobLimit << "\n";
|
||||
// std::cout << "POST LOCKED: " << postLocked << "\n";
|
||||
// std::cout << "CHAN BAL: " << channelBalance(*env.current(), chan) << "\n";
|
||||
// std::cout << "CHAN AUTH: " << channelAmount(*env.current(), chan) << "\n";
|
||||
}
|
||||
// test Global Freeze
|
||||
{
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env.trust(USD(100000), alice);
|
||||
env.trust(USD(100000), bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env(pay(gw, bob, USD(10000)));
|
||||
env.close();
|
||||
|
||||
env(fset(gw, asfGlobalFreeze));
|
||||
env.close();
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, USD(1000), settleDelay, pk), ter(tecFROZEN));
|
||||
env.close();
|
||||
|
||||
env(fclear(gw, asfGlobalFreeze));
|
||||
env.close();
|
||||
|
||||
chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, USD(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
env(fset(gw, asfGlobalFreeze));
|
||||
env.close();
|
||||
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
|
||||
auto const delta = USD(10);
|
||||
auto reqBal = chanBal + delta;
|
||||
auto authAmt = reqBal + USD(100);
|
||||
// alice cannot claim - tl global freeze
|
||||
env(claim(alice, chan, reqBal, authAmt), ter(tecFROZEN));
|
||||
// bob cannot claim - tl global freeze
|
||||
auto sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt);
|
||||
env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ter(tecFROZEN));
|
||||
env.close();
|
||||
|
||||
env(fclear(gw, asfGlobalFreeze));
|
||||
env.close();
|
||||
|
||||
// alice can claim
|
||||
env(claim(alice, chan, reqBal, authAmt));
|
||||
env.close();
|
||||
|
||||
// update channel values for claim
|
||||
chanBal = channelBalance(*env.current(), chan);
|
||||
chanAmt = channelAmount(*env.current(), chan);
|
||||
reqBal = chanBal + delta;
|
||||
authAmt = reqBal + USD(100);
|
||||
// bob can claim
|
||||
sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt);
|
||||
env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
|
||||
env.close();
|
||||
}
|
||||
{
|
||||
// test pay more than locked amount
|
||||
// ie. has 10000, lock 1000 then try to pay 10000
|
||||
@@ -4617,21 +4954,6 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
env(create(alice, bob, USD(10000), settleDelay, pk),
|
||||
ter(tecUNFUNDED_PAYMENT));
|
||||
}
|
||||
{
|
||||
// test create paychan from issuer with ic
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, gw);
|
||||
env.close();
|
||||
env.trust(USD(100000), alice);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env.close();
|
||||
// Create a channel from gw to alice
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
env(create(gw, alice, USD(1000), settleDelay, pk),
|
||||
ter(temBAD_SRC_ACCOUNT));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4676,7 +4998,8 @@ struct PayChan_test : public beast::unit_test::suite
|
||||
testICAccountDelete(features);
|
||||
testICUsingTickets(features);
|
||||
testICAutoTL(features);
|
||||
testICAbuseTL(features);
|
||||
testICGateway(features);
|
||||
testICTLFeatures(features);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user