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:
David Schwartz
2014-05-27 12:57:49 -07:00
committed by Nik Bougalis
parent 9eb34f542c
commit 7b936de32c
17 changed files with 258 additions and 51 deletions

View File

@@ -124,7 +124,7 @@ OfferStream::step ()
// NIKB NOTE The calling code also checks the funds, how expensive is // NIKB NOTE The calling code also checks the funds, how expensive is
// looking up the funds twice? // looking up the funds twice?
Amount const owner_funds (view().accountFunds ( 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 // Check for unfunded offer
if (owner_funds <= zero) if (owner_funds <= zero)
@@ -133,7 +133,7 @@ OfferStream::step ()
// we haven't modified the balance and therefore the // we haven't modified the balance and therefore the
// offer is "found unfunded" versus "became unfunded" // offer is "found unfunded" versus "became unfunded"
if (view_cancel().accountFunds (m_offer.account(), 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()); view_cancel().offerDelete (entry->getIndex());
if (m_journal.trace) m_journal.trace << if (m_journal.trace) m_journal.trace <<

View File

@@ -78,7 +78,8 @@ Amounts
Taker::flow (Amounts amount, Offer const& offer, Account const& taker) Taker::flow (Amounts amount, Offer const& offer, Account const& taker)
{ {
// Limit taker's input by available funds less fees // 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 // Get fee rate paid by taker
std::uint32_t const taker_charge_rate (view ().rippleTransferRate ( 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 // Limit owner's output by available funds less fees
Amount const owner_funds (view ().accountFunds ( 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 // Get fee rate paid by owner
std::uint32_t const owner_charge_rate (view ().rippleTransferRate ( 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 // 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 TER

View File

@@ -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 () void Ledger::setImmutable ()
{ {
// Updates the hash and marks the ledger and its maps immutable // Updates the hash and marks the ledger and its maps immutable

View File

@@ -182,6 +182,8 @@ public:
mAccountStateMap->setLedgerSeq (mLedgerSeq); mAccountStateMap->setLedgerSeq (mLedgerSeq);
} }
bool enforceFreeze () const;
// ledger signature operations // ledger signature operations
void addRaw (Serializer & s) const; void addRaw (Serializer & s) const;
void setRaw (Serializer & s, bool hasPrefix); void setRaw (Serializer & s, bool hasPrefix);

View File

@@ -1221,7 +1221,10 @@ LedgerEntrySet::rippleQualityIn (
// negative. // negative.
// <-- IOU's account has of issuer. // <-- IOU's account has of issuer.
STAmount LedgerEntrySet::rippleHolds ( 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; STAmount saBalance;
SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE, SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE,
@@ -1231,6 +1234,10 @@ STAmount LedgerEntrySet::rippleHolds (
{ {
saBalance.clear ({currency, issuer}); saBalance.clear ({currency, issuer});
} }
else if ((zeroIfFrozen == fhZERO_IF_FROZEN) && isFrozen (account, currency, issuer))
{
saBalance.clear (IssueRef (currency, issuer));
}
else if (account > issuer) else if (account > issuer)
{ {
saBalance = sleRippleState->getFieldAmount (sfBalance); saBalance = sleRippleState->getFieldAmount (sfBalance);
@@ -1252,7 +1259,10 @@ STAmount LedgerEntrySet::rippleHolds (
// //
// <-- saAmount: amount of currency held by account. May be negative. // <-- saAmount: amount of currency held by account. May be negative.
STAmount LedgerEntrySet::accountHolds ( 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; STAmount saAmount;
@@ -1282,7 +1292,7 @@ STAmount LedgerEntrySet::accountHolds (
} }
else else
{ {
saAmount = rippleHolds (account, currency, issuer); saAmount = rippleHolds (account, currency, issuer, zeroIfFrozen);
WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" << WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" <<
" account=" << to_string (account) << " account=" << to_string (account) <<
@@ -1292,6 +1302,46 @@ STAmount LedgerEntrySet::accountHolds (
return saAmount; 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. // Returns the funds available for account for a currency/issuer.
// Use when you need a default for rippling account's currency. // Use when you need a default for rippling account's currency.
// XXX Should take into account quality? // 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 // If the issuer is the same as account, funds are unlimited, use result is
// saDefault. // saDefault.
STAmount LedgerEntrySet::accountFunds ( STAmount LedgerEntrySet::accountFunds (
Account const& account, const STAmount& saDefault) Account const& account, const STAmount& saDefault, FreezeHandling zeroIfFrozen)
{ {
STAmount saFunds; STAmount saFunds;
@@ -1317,7 +1367,8 @@ STAmount LedgerEntrySet::accountFunds (
else else
{ {
saFunds = accountHolds ( saFunds = accountHolds (
account, saDefault.getCurrency (), saDefault.getIssuer ()); account, saDefault.getCurrency (), saDefault.getIssuer (),
zeroIfFrozen);
WriteLog (lsTRACE, LedgerEntrySet) << "accountFunds:" << WriteLog (lsTRACE, LedgerEntrySet) << "accountFunds:" <<
" account=" << to_string (account) << " account=" << to_string (account) <<

View File

@@ -51,6 +51,12 @@ enum LedgerEntryAction
taaCREATE, // Newly created. taaCREATE, // Newly created.
}; };
enum FreezeHandling
{
fhIGNORE_FREEZE,
fhZERO_IF_FROZEN
};
class LedgerEntrySetEntry class LedgerEntrySetEntry
: public CountedObject <LedgerEntrySetEntry> : public CountedObject <LedgerEntrySetEntry>
{ {
@@ -145,6 +151,11 @@ public:
return mLedger; return mLedger;
} }
bool enforceFreeze () const
{
return mLedger->enforceFreeze ();
}
// basic entry functions // basic entry functions
SLE::pointer getEntry (uint256 const & index, LedgerEntryAction&); SLE::pointer getEntry (uint256 const & index, LedgerEntryAction&);
LedgerEntryAction hasEntry (uint256 const & index) const; LedgerEntryAction hasEntry (uint256 const & index) const;
@@ -221,6 +232,13 @@ public:
sfLowQualityOut, sfHighQualityOut); sfLowQualityOut, sfHighQualityOut);
} }
bool isFrozen (
Account const& account,
Currency const& currency,
Account const& issuer);
bool isGlobalFrozen (const Account & issuer);
STAmount rippleTransferFee ( STAmount rippleTransferFee (
Account const& uSenderID, Account const& uReceiverID, Account const& uSenderID, Account const& uReceiverID,
Account const& issuer, const STAmount & saAmount); Account const& issuer, const STAmount & saAmount);
@@ -231,9 +249,9 @@ public:
STAmount accountHolds ( STAmount accountHolds (
Account const& account, Currency const& currency, Account const& account, Currency const& currency,
Account const& issuer); Account const& issuer, FreezeHandling freezeHandling);
STAmount accountFunds ( STAmount accountFunds (
Account const& account, const STAmount & saDefault); Account const& account, const STAmount & saDefault, FreezeHandling freezeHandling);
TER accountSend ( TER accountSend (
Account const& uSenderID, Account const& uReceiverID, Account const& uSenderID, Account const& uReceiverID,
const STAmount & saAmount); const STAmount & saAmount);
@@ -326,7 +344,7 @@ private:
STAmount rippleHolds ( STAmount rippleHolds (
Account const& account, Currency const& currency, Account const& account, Currency const& currency,
Account const& issuer); Account const& issuer, FreezeHandling zeroIfFrozen);
}; };
} // ripple } // ripple

View File

@@ -3056,6 +3056,9 @@ void NetworkOPsImp::getBookPage (
LedgerEntrySet lesActive (lpLedger, tapNONE, true); LedgerEntrySet lesActive (lpLedger, tapNONE, true);
const bool bGlobalFreeze = lesActive.isGlobalFrozen (book.out.account) ||
lesActive.isGlobalFrozen (book.in.account);
bool bDone = false; bool bDone = false;
bool bDirectAdvance = true; bool bDirectAdvance = true;
@@ -3120,6 +3123,12 @@ void NetworkOPsImp::getBookPage (
// funded. // funded.
saOwnerFunds = saTakerGets; 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 else
{ {
auto umBalanceEntry = umBalance.find (uOfferOwnerID); auto umBalanceEntry = umBalance.find (uOfferOwnerID);
@@ -3135,7 +3144,7 @@ void NetworkOPsImp::getBookPage (
saOwnerFunds = lesActive.accountHolds ( saOwnerFunds = lesActive.accountHolds (
uOfferOwnerID, book.out.currency, uOfferOwnerID, book.out.currency,
book.out.account); book.out.account, fhZERO_IF_FROZEN);
if (saOwnerFunds < zero) if (saOwnerFunds < zero)
{ {
@@ -3259,6 +3268,10 @@ void NetworkOPsImp::getBookPage (
auto uTransferRate = lesActive.rippleTransferRate (book.out.account); 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 ()) while (iLeft-- > 0 && obIterator.nextOffer ())
{ {
@@ -3276,6 +3289,12 @@ void NetworkOPsImp::getBookPage (
// If offer is selling issuer's own IOUs, it is fully funded. // If offer is selling issuer's own IOUs, it is fully funded.
saOwnerFunds = saTakerGets; 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 else
{ {
auto umBalanceEntry = umBalance.find (uOfferOwnerID); auto umBalanceEntry = umBalance.find (uOfferOwnerID);
@@ -3291,7 +3310,7 @@ void NetworkOPsImp::getBookPage (
// Did not find balance in table. // Did not find balance in table.
saOwnerFunds = lesActive.accountHolds ( saOwnerFunds = lesActive.accountHolds (
uOfferOwnerID, book.out.currency, book.out.account); uOfferOwnerID, book.out.currency, book.out.account, fhZERO_IF_FROZEN);
if (saOwnerFunds.isNegative ()) if (saOwnerFunds.isNegative ())
{ {

View File

@@ -602,6 +602,67 @@ TER PathState::expandPath (
return terStatus; 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 /** Check if a sequence of three accounts violates the no ripple constrains
[first] -> [second] -> [third] [first] -> [second] -> [third]
Disallowed if 'second' set no ripple on [first]->[second] and 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 // Loop through all nodes that have a prior node and successor nodes
// These are the nodes whose no ripple constratints could be violated // These are the nodes whose no ripple constraints could be violated
for (auto i = 1; i < nodes_.size() - 1; ++i) for (int i = 1; i < nodes_.size() - 1; ++i)
{ {
if (nodes_[i - 1].isAccount() && if (nodes_[i - 1].isAccount() &&
nodes_[i].isAccount() && nodes_[i].isAccount() &&

View File

@@ -97,6 +97,8 @@ class PathState : public CountedObject <PathState>
TER checkNoRipple (Account const& destinationAccountID, TER checkNoRipple (Account const& destinationAccountID,
Account const& sourceAccountID); Account const& sourceAccountID);
void checkFreeze ();
static bool lessPriority (PathState& lhs, PathState& rhs); static bool lessPriority (PathState& lhs, PathState& rhs);
LedgerEntrySet& ledgerEntries() { return lesEntries; } LedgerEntrySet& ledgerEntries() { return lesEntries; }

View File

@@ -507,31 +507,38 @@ int Pathfinder::getPathsOut (
int aFlags = sleAccount->getFieldU32(sfFlags); int aFlags = sleAccount->getFieldU32(sfFlags);
bool const bAuthRequired = (aFlags & lsfRequireAuth) != 0; bool const bAuthRequired = (aFlags & lsfRequireAuth) != 0;
bool const bFrozen = (aFlags & lsfGlobalFreeze) != 0;
int count = 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; mPOMap[currencyAccount] = count;
return count; return count;

View File

@@ -29,7 +29,10 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode)
saDstAmountReq_, saMaxAmountReq_); saDstAmountReq_, saMaxAmountReq_);
if (!pathState) if (!pathState)
{
resultCode = temUNKNOWN;
return false; return false;
}
pathState->expandPath ( pathState->expandPath (
mActiveLedger, mActiveLedger,
@@ -37,8 +40,11 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode)
uDstAccountID_, uDstAccountID_,
uSrcAccountID_); uSrcAccountID_);
if (tesSUCCESS == pathState->status()) if (pathState->status() == tesSUCCESS)
pathState->checkNoRipple (uDstAccountID_, uSrcAccountID_); pathState->checkNoRipple (uDstAccountID_, uSrcAccountID_);
if (pathState->status() == tesSUCCESS && mActiveLedger.enforceFreeze ())
pathState->checkFreeze ();
pathState->setIndex (pathStateList_.size ()); pathState->setIndex (pathStateList_.size ());
@@ -47,17 +53,18 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode)
<< " status: " << transToken (pathState->status()); << " status: " << transToken (pathState->status());
// Return if malformed. // Return if malformed.
if (isTemMalformed (pathState->status())) { if (isTemMalformed (pathState->status()))
{
resultCode = pathState->status(); resultCode = pathState->status();
return false; return false;
} }
if (tesSUCCESS == pathState->status()) if (pathState->status () == tesSUCCESS)
{ {
resultCode = tesSUCCESS; resultCode = pathState->status();
pathStateList_.push_back (pathState); pathStateList_.push_back (pathState);
} }
else if (terNO_LINE != pathState->status()) else if (pathState->status () != terNO_LINE)
{ {
resultCode = pathState->status(); resultCode = pathState->status();
} }
@@ -100,7 +107,6 @@ TER RippleCalc::rippleCalculate ()
// nodes. // nodes.
// XXX Might also make a XRP bridge by default. // XXX Might also make a XRP bridge by default.
WriteLog (lsTRACE, RippleCalc) WriteLog (lsTRACE, RippleCalc)
<< "rippleCalc: Paths in set: " << spsPaths_.size (); << "rippleCalc: Paths in set: " << spsPaths_.size ();

View File

@@ -124,7 +124,8 @@ TER PathCursor::advanceNode (bool const bReverse) const
// Funds left. // Funds left.
node().saOfferFunds = ledger().accountFunds ( node().saOfferFunds = ledger().accountFunds (
node().offerOwnerAccount_, node().offerOwnerAccount_,
node().saTakerGets); node().saTakerGets,
fhZERO_IF_FROZEN);
node().bFundsDirty = false; node().bFundsDirty = false;
WriteLog (lsTRACE, RippleCalc) WriteLog (lsTRACE, RippleCalc)
@@ -321,7 +322,9 @@ TER PathCursor::advanceNode (bool const bReverse) const
// Only the current node is allowed to use the source. // Only the current node is allowed to use the source.
node().saOfferFunds = ledger().accountFunds ( node().saOfferFunds = ledger().accountFunds (
node().offerOwnerAccount_, node().saTakerGets); node().offerOwnerAccount_,
node().saTakerGets,
fhZERO_IF_FROZEN);
// Funds held. // Funds held.
if (node().saOfferFunds <= zero) if (node().saOfferFunds <= zero)

View File

@@ -352,7 +352,8 @@ TER PathCursor::forwardLiquidityForAccount () const
ledger().accountHolds ( ledger().accountHolds (
node().account_, node().account_,
xrpCurrency(), xrpCurrency(),
xrpAccount())); xrpAccount(),
fhIGNORE_FREEZE)); // XRP can't be frozen
} }

View File

@@ -224,7 +224,15 @@ CreateOffer::doApply ()
terResult = temBAD_ISSUER; 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 << m_journal.warning <<
"delay: Offers must be at least partially funded."; "delay: Offers must be at least partially funded.";
@@ -349,8 +357,8 @@ CreateOffer::doApply ()
"takeOffers: mTxnAccountID=" << "takeOffers: mTxnAccountID=" <<
to_string (mTxnAccountID); to_string (mTxnAccountID);
m_journal.debug << m_journal.debug <<
"takeOffers: FUNDS=" << "takeOffers: FUNDS=" << view.accountFunds (
view.accountFunds (mTxnAccountID, saTakerGets).getFullText (); mTxnAccountID, saTakerGets, fhZERO_IF_FROZEN).getFullText ();
} }
if (saTakerPays < zero || saTakerGets < zero) if (saTakerPays < zero || saTakerGets < zero)

View File

@@ -61,8 +61,9 @@ CreateOfferBridged::crossOffers (
(options.passive? "passive" : "") << std::endl << (options.passive? "passive" : "") << std::endl <<
" taker: " << taker.account() << std::endl << " taker: " << taker.account() << std::endl <<
" balances: " << " balances: " <<
view.accountFunds (taker.account(), taker_amount.in) << ", " << view.accountFunds (taker.account(), taker_amount.in, fhIGNORE_FREEZE)
view.accountFunds (taker.account(), taker_amount.out); << ", " <<
view.accountFunds (taker.account(), taker_amount.out, fhIGNORE_FREEZE);
TER cross_result (tesSUCCESS); TER cross_result (tesSUCCESS);

View File

@@ -51,6 +51,7 @@ bool transResultInfo (TER terCode, std::string& strToken, std::string& strHuman)
{ tecNO_AUTH, "tecNO_AUTH", "Not authorized to hold asset." }, { tecNO_AUTH, "tecNO_AUTH", "Not authorized to hold asset." },
{ tecNO_LINE, "tecNO_LINE", "No such line." }, { tecNO_LINE, "tecNO_LINE", "No such line." },
{ tecINSUFF_FEE, "tecINSUFF_FEE", "Insufficient balance to pay fee." }, { tecINSUFF_FEE, "tecINSUFF_FEE", "Insufficient balance to pay fee." },
{ tecFROZEN, "tecFROZEN", "Asset is frozen." },
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger." }, { tefALREADY, "tefALREADY", "The exact transaction was already in this ledger." },
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." }, { tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },

View File

@@ -184,6 +184,7 @@ enum TER // aka TransactionEngineResult
tecNO_AUTH = 134, tecNO_AUTH = 134,
tecNO_LINE = 135, tecNO_LINE = 135,
tecINSUFF_FEE = 136, tecINSUFF_FEE = 136,
tecFROZEN = 137,
}; };
inline bool isTelLocal(TER x) inline bool isTelLocal(TER x)