mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
Freeze enforcing: (RIPD-399)
* Set enforce date: September 15, 2014 * Enforce in stand alone mode * Enforce at source * Enforce intermediary nodes * Enforce global freeze in get paths out * Enforce global freeze in create offer * Don't consider frozen links a path out * Handle in getBookPage * Enforce in new offer transactors
This commit is contained in:
committed by
Nik Bougalis
parent
9eb34f542c
commit
7b936de32c
@@ -124,7 +124,7 @@ OfferStream::step ()
|
||||
// NIKB NOTE The calling code also checks the funds, how expensive is
|
||||
// looking up the funds twice?
|
||||
Amount const owner_funds (view().accountFunds (
|
||||
m_offer.account(), m_offer.amount().out));
|
||||
m_offer.account(), m_offer.amount().out, fhZERO_IF_FROZEN));
|
||||
|
||||
// Check for unfunded offer
|
||||
if (owner_funds <= zero)
|
||||
@@ -133,7 +133,7 @@ OfferStream::step ()
|
||||
// we haven't modified the balance and therefore the
|
||||
// offer is "found unfunded" versus "became unfunded"
|
||||
if (view_cancel().accountFunds (m_offer.account(),
|
||||
m_offer.amount().out) == owner_funds)
|
||||
m_offer.amount().out, fhZERO_IF_FROZEN) == owner_funds)
|
||||
{
|
||||
view_cancel().offerDelete (entry->getIndex());
|
||||
if (m_journal.trace) m_journal.trace <<
|
||||
|
||||
@@ -78,7 +78,8 @@ Amounts
|
||||
Taker::flow (Amounts amount, Offer const& offer, Account const& taker)
|
||||
{
|
||||
// Limit taker's input by available funds less fees
|
||||
Amount const taker_funds (view ().accountFunds (taker, amount.in));
|
||||
Amount const taker_funds (view ().accountFunds (
|
||||
taker, amount.in, fhZERO_IF_FROZEN));
|
||||
|
||||
// Get fee rate paid by taker
|
||||
std::uint32_t const taker_charge_rate (view ().rippleTransferRate (
|
||||
@@ -102,7 +103,7 @@ Taker::flow (Amounts amount, Offer const& offer, Account const& taker)
|
||||
|
||||
// Limit owner's output by available funds less fees
|
||||
Amount const owner_funds (view ().accountFunds (
|
||||
offer.account (), owner_amount.out));
|
||||
offer.account (), owner_amount.out, fhZERO_IF_FROZEN));
|
||||
|
||||
// Get fee rate paid by owner
|
||||
std::uint32_t const owner_charge_rate (view ().rippleTransferRate (
|
||||
@@ -210,7 +211,8 @@ Taker::done () const
|
||||
}
|
||||
|
||||
// We are finished if the taker is out of funds
|
||||
return view().accountFunds (account(), m_remain.in) <= zero;
|
||||
return view().accountFunds (
|
||||
account(), m_remain.in, fhZERO_IF_FROZEN) <= zero;
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
@@ -257,6 +257,30 @@ Ledger::~Ledger ()
|
||||
}
|
||||
}
|
||||
|
||||
bool Ledger::enforceFreeze () const
|
||||
{
|
||||
|
||||
// Temporarily, the freze code can run in either
|
||||
// enforcing mode or non-enforcing mode. In
|
||||
// non-enforcing mode, freeze flags can be
|
||||
// manipulated, but freezing is not actually
|
||||
// enforced. Once freeze enforcing has been
|
||||
// enabled, this function can be removed
|
||||
|
||||
// Let freeze enforcement be tested
|
||||
// If you wish to test non-enforcing mode,
|
||||
// you must remove this line
|
||||
if (getConfig().RUN_STANDALONE)
|
||||
return true;
|
||||
|
||||
// Freeze enforcing date is September 15, 2014
|
||||
static std::uint32_t const enforceDate =
|
||||
iToSeconds (boost::posix_time::ptime (
|
||||
boost::gregorian::date (2014, boost::gregorian::Sep, 15)));
|
||||
|
||||
return mParentCloseTime >= enforceDate;
|
||||
}
|
||||
|
||||
void Ledger::setImmutable ()
|
||||
{
|
||||
// Updates the hash and marks the ledger and its maps immutable
|
||||
|
||||
@@ -182,6 +182,8 @@ public:
|
||||
mAccountStateMap->setLedgerSeq (mLedgerSeq);
|
||||
}
|
||||
|
||||
bool enforceFreeze () const;
|
||||
|
||||
// ledger signature operations
|
||||
void addRaw (Serializer & s) const;
|
||||
void setRaw (Serializer & s, bool hasPrefix);
|
||||
|
||||
@@ -1221,7 +1221,10 @@ LedgerEntrySet::rippleQualityIn (
|
||||
// negative.
|
||||
// <-- IOU's account has of issuer.
|
||||
STAmount LedgerEntrySet::rippleHolds (
|
||||
Account const& account, Currency const& currency, Account const& issuer)
|
||||
Account const& account,
|
||||
Currency const& currency,
|
||||
Account const& issuer,
|
||||
FreezeHandling zeroIfFrozen)
|
||||
{
|
||||
STAmount saBalance;
|
||||
SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE,
|
||||
@@ -1231,6 +1234,10 @@ STAmount LedgerEntrySet::rippleHolds (
|
||||
{
|
||||
saBalance.clear ({currency, issuer});
|
||||
}
|
||||
else if ((zeroIfFrozen == fhZERO_IF_FROZEN) && isFrozen (account, currency, issuer))
|
||||
{
|
||||
saBalance.clear (IssueRef (currency, issuer));
|
||||
}
|
||||
else if (account > issuer)
|
||||
{
|
||||
saBalance = sleRippleState->getFieldAmount (sfBalance);
|
||||
@@ -1252,7 +1259,10 @@ STAmount LedgerEntrySet::rippleHolds (
|
||||
//
|
||||
// <-- saAmount: amount of currency held by account. May be negative.
|
||||
STAmount LedgerEntrySet::accountHolds (
|
||||
Account const& account, Currency const& currency, Account const& issuer)
|
||||
Account const& account,
|
||||
Currency const& currency,
|
||||
Account const& issuer,
|
||||
FreezeHandling zeroIfFrozen)
|
||||
{
|
||||
STAmount saAmount;
|
||||
|
||||
@@ -1282,7 +1292,7 @@ STAmount LedgerEntrySet::accountHolds (
|
||||
}
|
||||
else
|
||||
{
|
||||
saAmount = rippleHolds (account, currency, issuer);
|
||||
saAmount = rippleHolds (account, currency, issuer, zeroIfFrozen);
|
||||
|
||||
WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" <<
|
||||
" account=" << to_string (account) <<
|
||||
@@ -1292,6 +1302,46 @@ STAmount LedgerEntrySet::accountHolds (
|
||||
return saAmount;
|
||||
}
|
||||
|
||||
bool LedgerEntrySet::isGlobalFrozen (const Account& issuer)
|
||||
{
|
||||
if (!enforceFreeze () || isXRP (issuer))
|
||||
return false;
|
||||
|
||||
SLE::pointer sle = entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (issuer));
|
||||
if (sle && sle->isFlag (lsfGlobalFreeze))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Can the specified account spend the specified currency issued by
|
||||
// the specified issuer or does the freeze flag prohibit it?
|
||||
bool LedgerEntrySet::isFrozen(
|
||||
Account const& account,
|
||||
Currency const& currency,
|
||||
Account const& issuer)
|
||||
{
|
||||
if (!enforceFreeze () || isXRP (currency))
|
||||
return false;
|
||||
|
||||
SLE::pointer sle = entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (issuer));
|
||||
if (sle && sle->isFlag (lsfGlobalFreeze))
|
||||
return true;
|
||||
|
||||
if (issuer != account)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
sle = entryCache (ltRIPPLE_STATE,
|
||||
Ledger::getRippleStateIndex (account, issuer, currency));
|
||||
if (sle && sle->isFlag ((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the funds available for account for a currency/issuer.
|
||||
// Use when you need a default for rippling account's currency.
|
||||
// XXX Should take into account quality?
|
||||
@@ -1301,7 +1351,7 @@ STAmount LedgerEntrySet::accountHolds (
|
||||
// If the issuer is the same as account, funds are unlimited, use result is
|
||||
// saDefault.
|
||||
STAmount LedgerEntrySet::accountFunds (
|
||||
Account const& account, const STAmount& saDefault)
|
||||
Account const& account, const STAmount& saDefault, FreezeHandling zeroIfFrozen)
|
||||
{
|
||||
STAmount saFunds;
|
||||
|
||||
@@ -1317,7 +1367,8 @@ STAmount LedgerEntrySet::accountFunds (
|
||||
else
|
||||
{
|
||||
saFunds = accountHolds (
|
||||
account, saDefault.getCurrency (), saDefault.getIssuer ());
|
||||
account, saDefault.getCurrency (), saDefault.getIssuer (),
|
||||
zeroIfFrozen);
|
||||
|
||||
WriteLog (lsTRACE, LedgerEntrySet) << "accountFunds:" <<
|
||||
" account=" << to_string (account) <<
|
||||
|
||||
@@ -51,6 +51,12 @@ enum LedgerEntryAction
|
||||
taaCREATE, // Newly created.
|
||||
};
|
||||
|
||||
enum FreezeHandling
|
||||
{
|
||||
fhIGNORE_FREEZE,
|
||||
fhZERO_IF_FROZEN
|
||||
};
|
||||
|
||||
class LedgerEntrySetEntry
|
||||
: public CountedObject <LedgerEntrySetEntry>
|
||||
{
|
||||
@@ -145,6 +151,11 @@ public:
|
||||
return mLedger;
|
||||
}
|
||||
|
||||
bool enforceFreeze () const
|
||||
{
|
||||
return mLedger->enforceFreeze ();
|
||||
}
|
||||
|
||||
// basic entry functions
|
||||
SLE::pointer getEntry (uint256 const & index, LedgerEntryAction&);
|
||||
LedgerEntryAction hasEntry (uint256 const & index) const;
|
||||
@@ -221,6 +232,13 @@ public:
|
||||
sfLowQualityOut, sfHighQualityOut);
|
||||
}
|
||||
|
||||
bool isFrozen (
|
||||
Account const& account,
|
||||
Currency const& currency,
|
||||
Account const& issuer);
|
||||
|
||||
bool isGlobalFrozen (const Account & issuer);
|
||||
|
||||
STAmount rippleTransferFee (
|
||||
Account const& uSenderID, Account const& uReceiverID,
|
||||
Account const& issuer, const STAmount & saAmount);
|
||||
@@ -231,9 +249,9 @@ public:
|
||||
|
||||
STAmount accountHolds (
|
||||
Account const& account, Currency const& currency,
|
||||
Account const& issuer);
|
||||
Account const& issuer, FreezeHandling freezeHandling);
|
||||
STAmount accountFunds (
|
||||
Account const& account, const STAmount & saDefault);
|
||||
Account const& account, const STAmount & saDefault, FreezeHandling freezeHandling);
|
||||
TER accountSend (
|
||||
Account const& uSenderID, Account const& uReceiverID,
|
||||
const STAmount & saAmount);
|
||||
@@ -326,7 +344,7 @@ private:
|
||||
|
||||
STAmount rippleHolds (
|
||||
Account const& account, Currency const& currency,
|
||||
Account const& issuer);
|
||||
Account const& issuer, FreezeHandling zeroIfFrozen);
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -3056,6 +3056,9 @@ void NetworkOPsImp::getBookPage (
|
||||
|
||||
LedgerEntrySet lesActive (lpLedger, tapNONE, true);
|
||||
|
||||
const bool bGlobalFreeze = lesActive.isGlobalFrozen (book.out.account) ||
|
||||
lesActive.isGlobalFrozen (book.in.account);
|
||||
|
||||
bool bDone = false;
|
||||
bool bDirectAdvance = true;
|
||||
|
||||
@@ -3120,6 +3123,12 @@ void NetworkOPsImp::getBookPage (
|
||||
// funded.
|
||||
saOwnerFunds = saTakerGets;
|
||||
}
|
||||
else if (bGlobalFreeze)
|
||||
{
|
||||
// If either asset is globally frozen, consider all offers
|
||||
// that aren't ours to be totally unfunded
|
||||
saOwnerFunds.clear (IssueRef (book.out.currency, book.out.account));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto umBalanceEntry = umBalance.find (uOfferOwnerID);
|
||||
@@ -3135,7 +3144,7 @@ void NetworkOPsImp::getBookPage (
|
||||
|
||||
saOwnerFunds = lesActive.accountHolds (
|
||||
uOfferOwnerID, book.out.currency,
|
||||
book.out.account);
|
||||
book.out.account, fhZERO_IF_FROZEN);
|
||||
|
||||
if (saOwnerFunds < zero)
|
||||
{
|
||||
@@ -3259,6 +3268,10 @@ void NetworkOPsImp::getBookPage (
|
||||
|
||||
auto uTransferRate = lesActive.rippleTransferRate (book.out.account);
|
||||
|
||||
const bool bGlobalFreeze = lesActive.isGlobalFrozen (book.out.account) ||
|
||||
lesActive.isGlobalFrozen (book.in.account);
|
||||
|
||||
|
||||
while (iLeft-- > 0 && obIterator.nextOffer ())
|
||||
{
|
||||
|
||||
@@ -3276,6 +3289,12 @@ void NetworkOPsImp::getBookPage (
|
||||
// If offer is selling issuer's own IOUs, it is fully funded.
|
||||
saOwnerFunds = saTakerGets;
|
||||
}
|
||||
else if (bGlobalFreeze)
|
||||
{
|
||||
// If either asset is globally frozen, consider all offers
|
||||
// that aren't ours to be totally unfunded
|
||||
saOwnerFunds.clear (IssueRef (book.out.currency, book.out.account));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto umBalanceEntry = umBalance.find (uOfferOwnerID);
|
||||
@@ -3291,7 +3310,7 @@ void NetworkOPsImp::getBookPage (
|
||||
// Did not find balance in table.
|
||||
|
||||
saOwnerFunds = lesActive.accountHolds (
|
||||
uOfferOwnerID, book.out.currency, book.out.account);
|
||||
uOfferOwnerID, book.out.currency, book.out.account, fhZERO_IF_FROZEN);
|
||||
|
||||
if (saOwnerFunds.isNegative ())
|
||||
{
|
||||
|
||||
@@ -602,6 +602,67 @@ TER PathState::expandPath (
|
||||
return terStatus;
|
||||
}
|
||||
|
||||
|
||||
/** Check if an expanded path violates freeze rules */
|
||||
void PathState::checkFreeze()
|
||||
{
|
||||
assert (nodes_.size() >= 2);
|
||||
|
||||
// A path with no intermediaries -- pure issue/redeem
|
||||
// cannot be frozen.
|
||||
if (nodes_.size() == 2)
|
||||
return;
|
||||
|
||||
SLE::pointer sle;
|
||||
|
||||
for (std::size_t i = 0; i < (nodes_.size() - 1); ++i)
|
||||
{
|
||||
// Check each order book for a global freeze
|
||||
if (nodes_[i].uFlags & STPathElement::typeIssuer)
|
||||
{
|
||||
sle = lesEntries.entryCache (ltACCOUNT_ROOT,
|
||||
Ledger::getAccountRootIndex (nodes_[i].issue_.account));
|
||||
|
||||
if (sle && sle->isFlag (lsfGlobalFreeze))
|
||||
{
|
||||
terStatus = terNO_LINE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check each account change to make sure funds can leave
|
||||
if (nodes_[i].uFlags & STPathElement::typeAccount)
|
||||
{
|
||||
Currency const& currencyID = nodes_[i].issue_.currency;
|
||||
Account const& inAccount = nodes_[i].account_;
|
||||
Account const& outAccount = nodes_[i+1].account_;
|
||||
|
||||
if (inAccount != outAccount)
|
||||
{
|
||||
sle = lesEntries.entryCache (ltACCOUNT_ROOT,
|
||||
Ledger::getAccountRootIndex (outAccount));
|
||||
|
||||
if (sle && sle->isFlag (lsfGlobalFreeze))
|
||||
{
|
||||
terStatus = terNO_LINE;
|
||||
return;
|
||||
}
|
||||
|
||||
sle = lesEntries.entryCache (ltRIPPLE_STATE,
|
||||
Ledger::getRippleStateIndex (inAccount,
|
||||
outAccount, currencyID));
|
||||
|
||||
if (sle && sle->isFlag (
|
||||
(outAccount > inAccount) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
terStatus = terNO_LINE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a sequence of three accounts violates the no ripple constrains
|
||||
[first] -> [second] -> [third]
|
||||
Disallowed if 'second' set no ripple on [first]->[second] and
|
||||
@@ -716,8 +777,8 @@ TER PathState::checkNoRipple (
|
||||
}
|
||||
|
||||
// Loop through all nodes that have a prior node and successor nodes
|
||||
// These are the nodes whose no ripple constratints could be violated
|
||||
for (auto i = 1; i < nodes_.size() - 1; ++i)
|
||||
// These are the nodes whose no ripple constraints could be violated
|
||||
for (int i = 1; i < nodes_.size() - 1; ++i)
|
||||
{
|
||||
if (nodes_[i - 1].isAccount() &&
|
||||
nodes_[i].isAccount() &&
|
||||
|
||||
@@ -97,6 +97,8 @@ class PathState : public CountedObject <PathState>
|
||||
|
||||
TER checkNoRipple (Account const& destinationAccountID,
|
||||
Account const& sourceAccountID);
|
||||
void checkFreeze ();
|
||||
|
||||
static bool lessPriority (PathState& lhs, PathState& rhs);
|
||||
|
||||
LedgerEntrySet& ledgerEntries() { return lesEntries; }
|
||||
|
||||
@@ -507,31 +507,38 @@ int Pathfinder::getPathsOut (
|
||||
|
||||
int aFlags = sleAccount->getFieldU32(sfFlags);
|
||||
bool const bAuthRequired = (aFlags & lsfRequireAuth) != 0;
|
||||
bool const bFrozen = (aFlags & lsfGlobalFreeze) != 0;
|
||||
|
||||
int count = 0;
|
||||
AccountItems& rippleLines (mRLCache->getRippleLines (accountID));
|
||||
|
||||
for (auto const& item : rippleLines.getItems ())
|
||||
if (!bFrozen)
|
||||
{
|
||||
RippleState* rspEntry = (RippleState*) item.get ();
|
||||
for (auto const& item : mRLCache->getRippleLines (accountID).getItems ())
|
||||
{
|
||||
RippleState* rspEntry = (RippleState*) item.get ();
|
||||
|
||||
if (currencyID != rspEntry->getLimit ().getCurrency ())
|
||||
{
|
||||
if (currencyID != rspEntry->getLimit ().getCurrency ())
|
||||
{
|
||||
}
|
||||
else if (rspEntry->getBalance () <= zero &&
|
||||
(!rspEntry->getLimitPeer ()
|
||||
|| -rspEntry->getBalance () >= rspEntry->getLimitPeer ()
|
||||
|| (bAuthRequired && !rspEntry->getAuth ())))
|
||||
{
|
||||
}
|
||||
else if (isDstCurrency && (dstAccount == rspEntry->getAccountIDPeer ()))
|
||||
count += 10000; // count a path to the destination extra
|
||||
else if (rspEntry->getNoRipplePeer ())
|
||||
{
|
||||
// This probably isn't a useful path out
|
||||
}
|
||||
else if (rspEntry->getFreezePeer () && mLedger->enforceFreeze ())
|
||||
{
|
||||
// Not a useful path out
|
||||
}
|
||||
else
|
||||
++count;
|
||||
}
|
||||
else if (rspEntry->getBalance () <= zero &&
|
||||
(!rspEntry->getLimitPeer ()
|
||||
|| -rspEntry->getBalance () >= rspEntry->getLimitPeer ()
|
||||
|| (bAuthRequired && !rspEntry->getAuth ())))
|
||||
{
|
||||
}
|
||||
else if (isDstCurrency && (dstAccount == rspEntry->getAccountIDPeer ()))
|
||||
count += 10000; // count a path to the destination extra
|
||||
else if (rspEntry->getNoRipplePeer ())
|
||||
{
|
||||
// This probably isn't a useful path out
|
||||
}
|
||||
else
|
||||
++count;
|
||||
}
|
||||
mPOMap[currencyAccount] = count;
|
||||
return count;
|
||||
|
||||
@@ -29,7 +29,10 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode)
|
||||
saDstAmountReq_, saMaxAmountReq_);
|
||||
|
||||
if (!pathState)
|
||||
{
|
||||
resultCode = temUNKNOWN;
|
||||
return false;
|
||||
}
|
||||
|
||||
pathState->expandPath (
|
||||
mActiveLedger,
|
||||
@@ -37,8 +40,11 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode)
|
||||
uDstAccountID_,
|
||||
uSrcAccountID_);
|
||||
|
||||
if (tesSUCCESS == pathState->status())
|
||||
pathState->checkNoRipple (uDstAccountID_, uSrcAccountID_);
|
||||
if (pathState->status() == tesSUCCESS)
|
||||
pathState->checkNoRipple (uDstAccountID_, uSrcAccountID_);
|
||||
|
||||
if (pathState->status() == tesSUCCESS && mActiveLedger.enforceFreeze ())
|
||||
pathState->checkFreeze ();
|
||||
|
||||
pathState->setIndex (pathStateList_.size ());
|
||||
|
||||
@@ -47,17 +53,18 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode)
|
||||
<< " status: " << transToken (pathState->status());
|
||||
|
||||
// Return if malformed.
|
||||
if (isTemMalformed (pathState->status())) {
|
||||
if (isTemMalformed (pathState->status()))
|
||||
{
|
||||
resultCode = pathState->status();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tesSUCCESS == pathState->status())
|
||||
if (pathState->status () == tesSUCCESS)
|
||||
{
|
||||
resultCode = tesSUCCESS;
|
||||
resultCode = pathState->status();
|
||||
pathStateList_.push_back (pathState);
|
||||
}
|
||||
else if (terNO_LINE != pathState->status())
|
||||
else if (pathState->status () != terNO_LINE)
|
||||
{
|
||||
resultCode = pathState->status();
|
||||
}
|
||||
@@ -100,7 +107,6 @@ TER RippleCalc::rippleCalculate ()
|
||||
// nodes.
|
||||
// XXX Might also make a XRP bridge by default.
|
||||
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "rippleCalc: Paths in set: " << spsPaths_.size ();
|
||||
|
||||
|
||||
@@ -124,7 +124,8 @@ TER PathCursor::advanceNode (bool const bReverse) const
|
||||
// Funds left.
|
||||
node().saOfferFunds = ledger().accountFunds (
|
||||
node().offerOwnerAccount_,
|
||||
node().saTakerGets);
|
||||
node().saTakerGets,
|
||||
fhZERO_IF_FROZEN);
|
||||
node().bFundsDirty = false;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
@@ -321,7 +322,9 @@ TER PathCursor::advanceNode (bool const bReverse) const
|
||||
// Only the current node is allowed to use the source.
|
||||
|
||||
node().saOfferFunds = ledger().accountFunds (
|
||||
node().offerOwnerAccount_, node().saTakerGets);
|
||||
node().offerOwnerAccount_,
|
||||
node().saTakerGets,
|
||||
fhZERO_IF_FROZEN);
|
||||
// Funds held.
|
||||
|
||||
if (node().saOfferFunds <= zero)
|
||||
|
||||
@@ -352,7 +352,8 @@ TER PathCursor::forwardLiquidityForAccount () const
|
||||
ledger().accountHolds (
|
||||
node().account_,
|
||||
xrpCurrency(),
|
||||
xrpAccount()));
|
||||
xrpAccount(),
|
||||
fhIGNORE_FREEZE)); // XRP can't be frozen
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -224,7 +224,15 @@ CreateOffer::doApply ()
|
||||
|
||||
terResult = temBAD_ISSUER;
|
||||
}
|
||||
else if (view.accountFunds (mTxnAccountID, saTakerGets) <= zero)
|
||||
else if (view.isGlobalFrozen (uPaysIssuerID) || view.isGlobalFrozen (uGetsIssuerID))
|
||||
{
|
||||
m_journal.warning <<
|
||||
"Offer involves frozen asset";
|
||||
|
||||
terResult = tecFROZEN;
|
||||
}
|
||||
else if (view.accountFunds (
|
||||
mTxnAccountID, saTakerGets, fhZERO_IF_FROZEN) <= zero)
|
||||
{
|
||||
m_journal.warning <<
|
||||
"delay: Offers must be at least partially funded.";
|
||||
@@ -349,8 +357,8 @@ CreateOffer::doApply ()
|
||||
"takeOffers: mTxnAccountID=" <<
|
||||
to_string (mTxnAccountID);
|
||||
m_journal.debug <<
|
||||
"takeOffers: FUNDS=" <<
|
||||
view.accountFunds (mTxnAccountID, saTakerGets).getFullText ();
|
||||
"takeOffers: FUNDS=" << view.accountFunds (
|
||||
mTxnAccountID, saTakerGets, fhZERO_IF_FROZEN).getFullText ();
|
||||
}
|
||||
|
||||
if (saTakerPays < zero || saTakerGets < zero)
|
||||
|
||||
@@ -61,8 +61,9 @@ CreateOfferBridged::crossOffers (
|
||||
(options.passive? "passive" : "") << std::endl <<
|
||||
" taker: " << taker.account() << std::endl <<
|
||||
" balances: " <<
|
||||
view.accountFunds (taker.account(), taker_amount.in) << ", " <<
|
||||
view.accountFunds (taker.account(), taker_amount.out);
|
||||
view.accountFunds (taker.account(), taker_amount.in, fhIGNORE_FREEZE)
|
||||
<< ", " <<
|
||||
view.accountFunds (taker.account(), taker_amount.out, fhIGNORE_FREEZE);
|
||||
|
||||
TER cross_result (tesSUCCESS);
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ bool transResultInfo (TER terCode, std::string& strToken, std::string& strHuman)
|
||||
{ tecNO_AUTH, "tecNO_AUTH", "Not authorized to hold asset." },
|
||||
{ tecNO_LINE, "tecNO_LINE", "No such line." },
|
||||
{ tecINSUFF_FEE, "tecINSUFF_FEE", "Insufficient balance to pay fee." },
|
||||
{ tecFROZEN, "tecFROZEN", "Asset is frozen." },
|
||||
|
||||
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger." },
|
||||
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },
|
||||
|
||||
@@ -184,6 +184,7 @@ enum TER // aka TransactionEngineResult
|
||||
tecNO_AUTH = 134,
|
||||
tecNO_LINE = 135,
|
||||
tecINSUFF_FEE = 136,
|
||||
tecFROZEN = 137,
|
||||
};
|
||||
|
||||
inline bool isTelLocal(TER x)
|
||||
|
||||
Reference in New Issue
Block a user