add gateway feature

This commit is contained in:
dangell7
2023-01-23 02:27:17 -05:00
parent 17193d12c7
commit 8740108f99
5 changed files with 695 additions and 319 deletions

View File

@@ -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

View File

@@ -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];

View File

@@ -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);

View File

@@ -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:

View File

@@ -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: