mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 11:35:53 +00:00
Cache and apply account credits after payment processing (RIPD-821):
Credits made to any account during the processing of a payment are delayed until the payment completes, enforcing a new invariant: liquidity for any paths during a payment's execution may never increase. This eliminates the need for special code to handle a variety of corner cases where consuming liquidity in one path increases liquidity in others.
This commit is contained in:
@@ -1510,6 +1510,14 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\ledger\ConsensusTransSetSF.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\ConsensusTransSetSF.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\ledger\DeferredCredits.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\ledger\DeferredCredits.h">
|
||||||
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\ledger\DirectoryEntryIterator.cpp">
|
<ClCompile Include="..\..\src\ripple\app\ledger\DirectoryEntryIterator.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
@@ -1610,6 +1618,12 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\ledger\OrderBookIterator.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\OrderBookIterator.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\ledger\tests\DeferredCredits.test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\ledger\tests\Ledger_test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\ledger\tests\Ledger_test.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -2220,6 +2220,12 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\ledger\ConsensusTransSetSF.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\ConsensusTransSetSF.h">
|
||||||
<Filter>ripple\app\ledger</Filter>
|
<Filter>ripple\app\ledger</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\ledger\DeferredCredits.cpp">
|
||||||
|
<Filter>ripple\app\ledger</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\ledger\DeferredCredits.h">
|
||||||
|
<Filter>ripple\app\ledger</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\ledger\DirectoryEntryIterator.cpp">
|
<ClCompile Include="..\..\src\ripple\app\ledger\DirectoryEntryIterator.cpp">
|
||||||
<Filter>ripple\app\ledger</Filter>
|
<Filter>ripple\app\ledger</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -2298,6 +2304,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\ledger\OrderBookIterator.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\OrderBookIterator.h">
|
||||||
<Filter>ripple\app\ledger</Filter>
|
<Filter>ripple\app\ledger</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\ledger\tests\DeferredCredits.test.cpp">
|
||||||
|
<Filter>ripple\app\ledger\tests</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\ledger\tests\Ledger_test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\ledger\tests\Ledger_test.cpp">
|
||||||
<Filter>ripple\app\ledger\tests</Filter>
|
<Filter>ripple\app\ledger\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
136
src/ripple/app/ledger/DeferredCredits.cpp
Normal file
136
src/ripple/app/ledger/DeferredCredits.cpp
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <ripple/app/ledger/DeferredCredits.h>
|
||||||
|
#include <ripple/basics/Log.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
template <class TMap>
|
||||||
|
void maybeLogCredit (Account const& sender,
|
||||||
|
Account const& receiver,
|
||||||
|
STAmount const& amount,
|
||||||
|
TMap const& adjMap)
|
||||||
|
{
|
||||||
|
using std::get;
|
||||||
|
|
||||||
|
if (!ShouldLog (lsTRACE, DeferredCredits))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// write the balances to the log
|
||||||
|
std::stringstream str;
|
||||||
|
str << "assetXfer: " << sender << ", " << receiver << ", " << amount;
|
||||||
|
if (!adjMap.empty ())
|
||||||
|
{
|
||||||
|
str << " : ";
|
||||||
|
}
|
||||||
|
for (auto i = adjMap.begin (), e = adjMap.end ();
|
||||||
|
i != e; ++i)
|
||||||
|
{
|
||||||
|
if (i != adjMap.begin ())
|
||||||
|
{
|
||||||
|
str << ", ";
|
||||||
|
}
|
||||||
|
auto const& k(i->first);
|
||||||
|
auto const& v(i->second);
|
||||||
|
str << to_string (get<0> (k)) << " | " <<
|
||||||
|
to_string (get<1> (k)) << " | " <<
|
||||||
|
get<1> (v).getFullText () << " | " <<
|
||||||
|
get<0> (v).getFullText ();
|
||||||
|
}
|
||||||
|
WriteLog (lsTRACE, DeferredCredits) << str.str ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeferredCredits::credit (Account const& sender,
|
||||||
|
Account const& receiver,
|
||||||
|
STAmount const& amount)
|
||||||
|
{
|
||||||
|
using std::get;
|
||||||
|
|
||||||
|
WriteLog (lsTRACE, DeferredCredits)
|
||||||
|
<< "credit: " << sender << ", " << receiver << ", " << amount;
|
||||||
|
|
||||||
|
assert (sender != receiver);
|
||||||
|
assert (!amount.negative ());
|
||||||
|
|
||||||
|
auto const k = makeKey (sender, receiver, amount.getCurrency ());
|
||||||
|
auto i = map_.find (k);
|
||||||
|
if (i == map_.end ())
|
||||||
|
{
|
||||||
|
Value v;
|
||||||
|
|
||||||
|
if (sender < receiver)
|
||||||
|
{
|
||||||
|
get<1> (v) = amount;
|
||||||
|
get<0> (v) = amount.zeroed ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
get<1> (v) = amount.zeroed ();
|
||||||
|
get<0> (v) = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_[k] = v;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto& v = i->second;
|
||||||
|
if (sender < receiver)
|
||||||
|
get<1> (v) += amount;
|
||||||
|
else
|
||||||
|
get<0> (v) += amount;
|
||||||
|
}
|
||||||
|
maybeLogCredit (sender, receiver, amount, map_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the adjusted balance of main for the
|
||||||
|
// balance between main and other.
|
||||||
|
STAmount DeferredCredits::adjustedBalance (Account const& main,
|
||||||
|
Account const& other,
|
||||||
|
STAmount const& curBalance) const
|
||||||
|
{
|
||||||
|
using std::get;
|
||||||
|
STAmount result (curBalance);
|
||||||
|
|
||||||
|
Key const k = makeKey (main, other, curBalance.getCurrency ());
|
||||||
|
auto i = map_.find (k);
|
||||||
|
if (i != map_.end ())
|
||||||
|
{
|
||||||
|
auto const& v = i->second;
|
||||||
|
if (main < other)
|
||||||
|
{
|
||||||
|
result -= get<0> (v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result -= get<1> (v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLog (lsTRACE, DeferredCredits)
|
||||||
|
<< "adjustedBalance: " << main << ", " <<
|
||||||
|
other << ", " << curBalance << ", " << result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeferredCredits::clear ()
|
||||||
|
{
|
||||||
|
map_.clear ();
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/ripple/app/ledger/DeferredCredits.h
Normal file
59
src/ripple/app/ledger/DeferredCredits.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef RIPPLE_APP_LEDGER_CACHEDCREDITS_H_INCLUDED
|
||||||
|
#define RIPPLE_APP_LEDGER_CACHEDCREDITS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/protocol/UintTypes.h>
|
||||||
|
#include <ripple/protocol/STAmount.h>
|
||||||
|
#include <map>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
class DeferredCredits
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// lowAccount, highAccount
|
||||||
|
using Key = std::tuple<Account, Account, Currency>;
|
||||||
|
// lowAccountCredits, highAccountCredits
|
||||||
|
using Value = std::tuple<STAmount, STAmount>;
|
||||||
|
static inline
|
||||||
|
Key makeKey(Account const& a1, Account const& a2, Currency const& c)
|
||||||
|
{
|
||||||
|
if (a1 < a2)
|
||||||
|
return std::make_tuple(a1, a2, c);
|
||||||
|
else
|
||||||
|
return std::make_tuple(a2, a1, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<Key, Value> map_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void credit (Account const& sender,
|
||||||
|
Account const& receiver,
|
||||||
|
STAmount const& amount);
|
||||||
|
// Get the adjusted balance of main for the
|
||||||
|
// balance between main and other.
|
||||||
|
STAmount adjustedBalance (Account const& main,
|
||||||
|
Account const& other,
|
||||||
|
STAmount const& curBalance) const;
|
||||||
|
void clear ();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
#include <ripple/app/book/Quality.h>
|
#include <ripple/app/book/Quality.h>
|
||||||
#include <ripple/app/ledger/LedgerEntrySet.h>
|
#include <ripple/app/ledger/LedgerEntrySet.h>
|
||||||
|
#include <ripple/app/ledger/DeferredCredits.h>
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
#include <ripple/basics/StringUtilities.h>
|
#include <ripple/basics/StringUtilities.h>
|
||||||
#include <ripple/json/to_string.h>
|
#include <ripple/json/to_string.h>
|
||||||
@@ -40,6 +41,8 @@ void LedgerEntrySet::init (Ledger::ref ledger, uint256 const& transactionID,
|
|||||||
std::uint32_t ledgerID, TransactionEngineParams params)
|
std::uint32_t ledgerID, TransactionEngineParams params)
|
||||||
{
|
{
|
||||||
mEntries.clear ();
|
mEntries.clear ();
|
||||||
|
if (mDeferredCredits)
|
||||||
|
mDeferredCredits->clear ();
|
||||||
mLedger = ledger;
|
mLedger = ledger;
|
||||||
mSet.init (transactionID, ledgerID);
|
mSet.init (transactionID, ledgerID);
|
||||||
mParams = params;
|
mParams = params;
|
||||||
@@ -50,20 +53,24 @@ void LedgerEntrySet::clear ()
|
|||||||
{
|
{
|
||||||
mEntries.clear ();
|
mEntries.clear ();
|
||||||
mSet.clear ();
|
mSet.clear ();
|
||||||
|
if (mDeferredCredits)
|
||||||
|
mDeferredCredits->clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
LedgerEntrySet LedgerEntrySet::duplicate () const
|
LedgerEntrySet LedgerEntrySet::duplicate () const
|
||||||
{
|
{
|
||||||
return LedgerEntrySet (mLedger, mEntries, mSet, mSeq + 1);
|
return LedgerEntrySet (mLedger, mEntries, mSet, mSeq + 1, mDeferredCredits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LedgerEntrySet::swapWith (LedgerEntrySet& e)
|
void LedgerEntrySet::swapWith (LedgerEntrySet& e)
|
||||||
{
|
{
|
||||||
std::swap (mLedger, e.mLedger);
|
using std::swap;
|
||||||
|
swap (mLedger, e.mLedger);
|
||||||
mEntries.swap (e.mEntries);
|
mEntries.swap (e.mEntries);
|
||||||
mSet.swap (e.mSet);
|
mSet.swap (e.mSet);
|
||||||
std::swap (mParams, e.mParams);
|
swap (mParams, e.mParams);
|
||||||
std::swap (mSeq, e.mSeq);
|
swap (mSeq, e.mSeq);
|
||||||
|
swap (mDeferredCredits, e.mDeferredCredits);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find an entry in the set. If it has the wrong sequence number, copy it and update the sequence number.
|
// Find an entry in the set. If it has the wrong sequence number, copy it and update the sequence number.
|
||||||
@@ -1125,7 +1132,7 @@ STAmount LedgerEntrySet::rippleHolds (
|
|||||||
saBalance.setIssuer (issuer);
|
saBalance.setIssuer (issuer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return saBalance;
|
return adjustedBalance(account, issuer, saBalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the amount an account can spend without going into debt.
|
// Returns the amount an account can spend without going into debt.
|
||||||
@@ -1162,6 +1169,8 @@ STAmount LedgerEntrySet::accountHolds (
|
|||||||
" saAmount=" << saAmount.getFullText () <<
|
" saAmount=" << saAmount.getFullText () <<
|
||||||
" saBalance=" << saBalance.getFullText () <<
|
" saBalance=" << saBalance.getFullText () <<
|
||||||
" uReserve=" << uReserve;
|
" uReserve=" << uReserve;
|
||||||
|
|
||||||
|
return adjustedBalance(account, issuer, saAmount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1170,9 +1179,10 @@ STAmount LedgerEntrySet::accountHolds (
|
|||||||
WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" <<
|
WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" <<
|
||||||
" account=" << to_string (account) <<
|
" account=" << to_string (account) <<
|
||||||
" saAmount=" << saAmount.getFullText ();
|
" saAmount=" << saAmount.getFullText ();
|
||||||
|
|
||||||
|
return saAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return saAmount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LedgerEntrySet::isGlobalFrozen (Account const& issuer)
|
bool LedgerEntrySet::isGlobalFrozen (Account const& issuer)
|
||||||
@@ -1268,6 +1278,10 @@ TER LedgerEntrySet::trustCreate (
|
|||||||
const std::uint32_t uQualityIn,
|
const std::uint32_t uQualityIn,
|
||||||
const std::uint32_t uQualityOut)
|
const std::uint32_t uQualityOut)
|
||||||
{
|
{
|
||||||
|
WriteLog (lsTRACE, LedgerEntrySet)
|
||||||
|
<< "trustCreate: " << to_string (uSrcAccountID) << ", "
|
||||||
|
<< to_string (uDstAccountID) << ", " << saBalance.getFullText ();
|
||||||
|
|
||||||
auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
|
auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
|
||||||
auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
|
auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
|
||||||
|
|
||||||
@@ -1351,6 +1365,8 @@ TER LedgerEntrySet::trustCreate (
|
|||||||
|
|
||||||
// ONLY: Create ripple balance.
|
// ONLY: Create ripple balance.
|
||||||
sleRippleState->setFieldAmount (sfBalance, bSetHigh ? -saBalance : saBalance);
|
sleRippleState->setFieldAmount (sfBalance, bSetHigh ? -saBalance : saBalance);
|
||||||
|
|
||||||
|
cacheCredit (uSrcAccountID, uDstAccountID, saBalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
return terResult;
|
return terResult;
|
||||||
@@ -1396,6 +1412,42 @@ TER LedgerEntrySet::trustDelete (
|
|||||||
return terResult;
|
return terResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LedgerEntrySet::enableDeferredCredits (bool enable)
|
||||||
|
{
|
||||||
|
assert(enable == !mDeferredCredits);
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
|
{
|
||||||
|
mDeferredCredits.reset ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mDeferredCredits)
|
||||||
|
mDeferredCredits.emplace ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LedgerEntrySet::areCreditsDeferred () const
|
||||||
|
{
|
||||||
|
return static_cast<bool> (mDeferredCredits);
|
||||||
|
}
|
||||||
|
|
||||||
|
STAmount LedgerEntrySet::adjustedBalance (Account const& main,
|
||||||
|
Account const& other,
|
||||||
|
STAmount const& amount) const
|
||||||
|
{
|
||||||
|
if (mDeferredCredits)
|
||||||
|
return mDeferredCredits->adjustedBalance (main, other, amount);
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LedgerEntrySet::cacheCredit (Account const& sender,
|
||||||
|
Account const& receiver,
|
||||||
|
STAmount const& amount)
|
||||||
|
{
|
||||||
|
if (mDeferredCredits)
|
||||||
|
return mDeferredCredits->credit (sender, receiver, amount);
|
||||||
|
}
|
||||||
|
|
||||||
// Direct send w/o fees:
|
// Direct send w/o fees:
|
||||||
// - Redeeming IOUs and/or sending sender's own IOUs.
|
// - Redeeming IOUs and/or sending sender's own IOUs.
|
||||||
// - Create trust line of needed.
|
// - Create trust line of needed.
|
||||||
@@ -1459,6 +1511,8 @@ TER LedgerEntrySet::rippleCredit (
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
cacheCredit (uSenderID, uReceiverID, saAmount);
|
||||||
|
|
||||||
STAmount saBalance = sleRippleState->getFieldAmount (sfBalance);
|
STAmount saBalance = sleRippleState->getFieldAmount (sfBalance);
|
||||||
|
|
||||||
if (bSenderHigh)
|
if (bSenderHigh)
|
||||||
@@ -1634,6 +1688,8 @@ TER LedgerEntrySet::accountSend (
|
|||||||
return rippleSend (uSenderID, uReceiverID, saAmount, saActual);
|
return rippleSend (uSenderID, uReceiverID, saAmount, saActual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheCredit (uSenderID, uReceiverID, saAmount);
|
||||||
|
|
||||||
/* XRP send which does not check reserve and can do pure adjustment.
|
/* XRP send which does not check reserve and can do pure adjustment.
|
||||||
* Note that sender or receiver may be null and this not a mistake; this
|
* Note that sender or receiver may be null and this not a mistake; this
|
||||||
* setup is used during pathfinding and it is carefully controlled to
|
* setup is used during pathfinding and it is carefully controlled to
|
||||||
@@ -1819,6 +1875,8 @@ TER LedgerEntrySet::issue_iou (
|
|||||||
if (bSenderHigh)
|
if (bSenderHigh)
|
||||||
final_balance.negate ();
|
final_balance.negate ();
|
||||||
|
|
||||||
|
cacheCredit (issue.account, account, amount);
|
||||||
|
|
||||||
// Adjust the balance on the trust line if necessary. We do this even if we
|
// Adjust the balance on the trust line if necessary. We do this even if we
|
||||||
// are going to delete the line to reflect the correct balance at the time
|
// are going to delete the line to reflect the correct balance at the time
|
||||||
// of deletion.
|
// of deletion.
|
||||||
@@ -1884,6 +1942,8 @@ TER LedgerEntrySet::redeem_iou (
|
|||||||
if (bSenderHigh)
|
if (bSenderHigh)
|
||||||
final_balance.negate ();
|
final_balance.negate ();
|
||||||
|
|
||||||
|
cacheCredit (account, issue.account, amount);
|
||||||
|
|
||||||
// Adjust the balance on the trust line if necessary. We do this even if we
|
// Adjust the balance on the trust line if necessary. We do this even if we
|
||||||
// are going to delete the line to reflect the correct balance at the time
|
// are going to delete the line to reflect the correct balance at the time
|
||||||
// of deletion.
|
// of deletion.
|
||||||
@@ -1964,4 +2024,24 @@ rippleTransferRate (LedgerEntrySet& ledger, Account const& uSenderID,
|
|||||||
: rippleTransferRate (ledger, issuer);
|
: rippleTransferRate (ledger, issuer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScopedDeferCredits::ScopedDeferCredits (LedgerEntrySet& l)
|
||||||
|
: les_ (l), enabled_ (false)
|
||||||
|
{
|
||||||
|
if (!les_.areCreditsDeferred ())
|
||||||
|
{
|
||||||
|
WriteLog (lsTRACE, DeferredCredits) << "Enable";
|
||||||
|
les_.enableDeferredCredits (true);
|
||||||
|
enabled_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedDeferCredits::~ScopedDeferCredits ()
|
||||||
|
{
|
||||||
|
if (enabled_)
|
||||||
|
{
|
||||||
|
WriteLog (lsTRACE, DeferredCredits) << "Disable";
|
||||||
|
les_.enableDeferredCredits (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -21,8 +21,10 @@
|
|||||||
#define RIPPLE_APP_LEDGER_LEDGERENTRYSET_H_INCLUDED
|
#define RIPPLE_APP_LEDGER_LEDGERENTRYSET_H_INCLUDED
|
||||||
|
|
||||||
#include <ripple/app/ledger/Ledger.h>
|
#include <ripple/app/ledger/Ledger.h>
|
||||||
|
#include <ripple/app/ledger/DeferredCredits.h>
|
||||||
#include <ripple/basics/CountedObject.h>
|
#include <ripple/basics/CountedObject.h>
|
||||||
#include <ripple/protocol/STLedgerEntry.h>
|
#include <ripple/protocol/STLedgerEntry.h>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -114,6 +116,7 @@ public:
|
|||||||
void invalidate ()
|
void invalidate ()
|
||||||
{
|
{
|
||||||
mLedger.reset ();
|
mLedger.reset ();
|
||||||
|
mDeferredCredits.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValid () const
|
bool isValid () const
|
||||||
@@ -203,6 +206,9 @@ public:
|
|||||||
|
|
||||||
bool isGlobalFrozen (Account const& issuer);
|
bool isGlobalFrozen (Account const& issuer);
|
||||||
|
|
||||||
|
void enableDeferredCredits (bool enable=true);
|
||||||
|
bool areCreditsDeferred () const;
|
||||||
|
|
||||||
TER rippleCredit (
|
TER rippleCredit (
|
||||||
Account const& uSenderID, Account const& uReceiverID,
|
Account const& uSenderID, Account const& uReceiverID,
|
||||||
const STAmount & saAmount, bool bCheckIssuer = true);
|
const STAmount & saAmount, bool bCheckIssuer = true);
|
||||||
@@ -285,6 +291,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
Ledger::pointer mLedger;
|
Ledger::pointer mLedger;
|
||||||
std::map<uint256, LedgerEntrySetEntry> mEntries; // cannot be unordered!
|
std::map<uint256, LedgerEntrySetEntry> mEntries; // cannot be unordered!
|
||||||
|
// Defers credits made to accounts until later
|
||||||
|
boost::optional<DeferredCredits> mDeferredCredits;
|
||||||
|
|
||||||
typedef hash_map<uint256, SLE::pointer> NodeToLedgerEntry;
|
typedef hash_map<uint256, SLE::pointer> NodeToLedgerEntry;
|
||||||
|
|
||||||
@@ -295,9 +303,9 @@ private:
|
|||||||
|
|
||||||
LedgerEntrySet (
|
LedgerEntrySet (
|
||||||
Ledger::ref ledger, const std::map<uint256, LedgerEntrySetEntry>& e,
|
Ledger::ref ledger, const std::map<uint256, LedgerEntrySetEntry>& e,
|
||||||
const TransactionMetaSet & s, int m) :
|
const TransactionMetaSet & s, int m, boost::optional<DeferredCredits> const& ft) :
|
||||||
mLedger (ledger), mEntries (e), mSet (s), mParams (tapNONE), mSeq (m),
|
mLedger (ledger), mEntries (e), mDeferredCredits (ft), mSet (s), mParams (tapNONE),
|
||||||
mImmutable (false)
|
mSeq (m), mImmutable (false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SLE::pointer getForMod (
|
SLE::pointer getForMod (
|
||||||
@@ -328,6 +336,24 @@ private:
|
|||||||
|
|
||||||
bool checkState (SLE::pointer state, bool bSenderHigh,
|
bool checkState (SLE::pointer state, bool bSenderHigh,
|
||||||
Account const& sender, STAmount const& before, STAmount const& after);
|
Account const& sender, STAmount const& before, STAmount const& after);
|
||||||
|
|
||||||
|
STAmount adjustedBalance (Account const& main,
|
||||||
|
Account const& other,
|
||||||
|
STAmount const& amount) const;
|
||||||
|
|
||||||
|
void cacheCredit (Account const& sender,
|
||||||
|
Account const& receiver,
|
||||||
|
STAmount const& amount);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScopedDeferCredits
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LedgerEntrySet& les_;
|
||||||
|
bool enabled_;
|
||||||
|
public:
|
||||||
|
ScopedDeferCredits(LedgerEntrySet& l);
|
||||||
|
~ScopedDeferCredits ();
|
||||||
};
|
};
|
||||||
|
|
||||||
// NIKB FIXME: move these to the right place
|
// NIKB FIXME: move these to the right place
|
||||||
|
|||||||
291
src/ripple/app/ledger/tests/DeferredCredits.test.cpp
Normal file
291
src/ripple/app/ledger/tests/DeferredCredits.test.cpp
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <ripple/app/tests/common_ledger.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
class DeferredCredits_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Create paths so one path funds another path.
|
||||||
|
|
||||||
|
Two accounts: sender and receiver.
|
||||||
|
Two gateways: gw1 and gw2.
|
||||||
|
Sender and receiver both have trust lines to the gateways.
|
||||||
|
Sender has 2 gw1/USD and 4 gw2/USD.
|
||||||
|
Sender has offer to exchange 2 gw1 for gw2 and gw2 for gw1 1-for-1.
|
||||||
|
Paths are:
|
||||||
|
1) GW1 -> [OB GW1/USD->GW2/USD] -> GW2
|
||||||
|
2) GW2 -> [OB GW2/USD->GW1/USD] -> GW1
|
||||||
|
|
||||||
|
sender pays receiver 4 USD.
|
||||||
|
Path 1:
|
||||||
|
1) Sender exchanges 2 GW1/USD for 2 GW2/USD
|
||||||
|
2) Old code: the 2 GW1/USD is available to sender
|
||||||
|
New code: the 2 GW1/USD is not available until the
|
||||||
|
end of the transaction.
|
||||||
|
3) Receiver gets 2 GW2/USD
|
||||||
|
Path 2:
|
||||||
|
1) Old code: Sender exchanges 2 GW2/USD for 2 GW1/USD
|
||||||
|
2) Old code: Receiver get 2 GW1
|
||||||
|
2) New code: Path is dry because sender does not have any
|
||||||
|
GW1 to spend until the end of the transaction.
|
||||||
|
*/
|
||||||
|
void testSelfFunding ()
|
||||||
|
{
|
||||||
|
testcase ("selfFunding");
|
||||||
|
|
||||||
|
auto const keyType = KeyType::ed25519;
|
||||||
|
std::uint64_t const xrp = std::mega::num;
|
||||||
|
|
||||||
|
auto master = createAccount ("masterpassphrase", keyType);
|
||||||
|
|
||||||
|
Ledger::pointer LCL;
|
||||||
|
Ledger::pointer ledger;
|
||||||
|
std::tie (LCL, ledger) = createGenesisLedger (100000 * xrp, master);
|
||||||
|
|
||||||
|
auto accounts =
|
||||||
|
createAndFundAccountsWithFlags (master,
|
||||||
|
{"snd", "rcv", "gw1", "gw2"},
|
||||||
|
keyType,
|
||||||
|
10000 * xrp,
|
||||||
|
ledger,
|
||||||
|
LCL,
|
||||||
|
asfDefaultRipple);
|
||||||
|
auto& gw1 = accounts["gw1"];
|
||||||
|
auto& gw2 = accounts["gw2"];
|
||||||
|
auto& snd = accounts["snd"];
|
||||||
|
auto& rcv = accounts["rcv"];
|
||||||
|
|
||||||
|
close_and_advance (ledger, LCL);
|
||||||
|
|
||||||
|
trust (snd, gw1, "USD", 10, ledger);
|
||||||
|
trust (snd, gw2, "USD", 10, ledger);
|
||||||
|
trust (rcv, gw1, "USD", 100, ledger);
|
||||||
|
trust (rcv, gw2, "USD", 100, ledger);
|
||||||
|
|
||||||
|
pay (gw1, snd, "USD", "2", ledger);
|
||||||
|
pay (gw2, snd, "USD", "4", ledger);
|
||||||
|
|
||||||
|
verifyBalance (ledger, snd, Amount (2, "USD", gw1));
|
||||||
|
verifyBalance (ledger, snd, Amount (4, "USD", gw2));
|
||||||
|
|
||||||
|
close_and_advance (ledger, LCL);
|
||||||
|
|
||||||
|
createOfferWithFlags (snd,
|
||||||
|
Amount (2, "USD", gw1),
|
||||||
|
Amount (2, "USD", gw2),
|
||||||
|
ledger,
|
||||||
|
tfPassive);
|
||||||
|
createOfferWithFlags (snd,
|
||||||
|
Amount (2, "USD", gw2),
|
||||||
|
Amount (2, "USD", gw1),
|
||||||
|
ledger,
|
||||||
|
tfPassive);
|
||||||
|
|
||||||
|
close_and_advance (ledger, LCL);
|
||||||
|
|
||||||
|
Json::Value path;
|
||||||
|
path.append (createPath (gw1, OfferPathNode ("USD", gw2), gw2));
|
||||||
|
path.append (createPath (gw2, OfferPathNode ("USD", gw1), gw1));
|
||||||
|
|
||||||
|
payWithPath (snd, rcv, "USD", "4", ledger, path,
|
||||||
|
tfNoRippleDirect | tfPartialPayment);
|
||||||
|
|
||||||
|
verifyBalance (ledger, rcv, Amount (0, "USD", gw1));
|
||||||
|
verifyBalance (ledger, rcv, Amount (2, "USD", gw2));
|
||||||
|
|
||||||
|
pass ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void testSubtractCredits ()
|
||||||
|
{
|
||||||
|
testcase ("subtractCredits");
|
||||||
|
|
||||||
|
auto const keyType = KeyType::ed25519;
|
||||||
|
std::uint64_t const xrp = std::mega::num;
|
||||||
|
|
||||||
|
auto master = createAccount ("masterpassphrase", keyType);
|
||||||
|
|
||||||
|
Ledger::pointer LCL;
|
||||||
|
Ledger::pointer ledger;
|
||||||
|
std::tie (LCL, ledger) = createGenesisLedger (100000 * xrp, master);
|
||||||
|
|
||||||
|
auto accounts =
|
||||||
|
createAndFundAccountsWithFlags (master,
|
||||||
|
{"alice", "gw1", "gw2"},
|
||||||
|
keyType,
|
||||||
|
10000 * xrp,
|
||||||
|
ledger,
|
||||||
|
LCL,
|
||||||
|
asfDefaultRipple);
|
||||||
|
auto& gw1 = accounts["gw1"];
|
||||||
|
auto& gw2 = accounts["gw2"];
|
||||||
|
auto& alice = accounts["alice"];
|
||||||
|
|
||||||
|
close_and_advance (ledger, LCL);
|
||||||
|
|
||||||
|
trust (alice, gw1, "USD", 100, ledger);
|
||||||
|
trust (alice, gw2, "USD", 100, ledger);
|
||||||
|
|
||||||
|
pay (gw1, alice, "USD", "50", ledger);
|
||||||
|
pay (gw2, alice, "USD", "50", ledger);
|
||||||
|
|
||||||
|
verifyBalance (ledger, alice, Amount (50, "USD", gw1));
|
||||||
|
verifyBalance (ledger, alice, Amount (50, "USD", gw2));
|
||||||
|
|
||||||
|
ripple::Account const gw1Acc (gw1.pk.getAccountID ());
|
||||||
|
ripple::Account const aliceAcc (alice.pk.getAccountID ());
|
||||||
|
ripple::Currency const usd (to_currency ("USD"));
|
||||||
|
ripple::Issue const issue (usd, gw1Acc);
|
||||||
|
STAmount const toCredit (issue, 30);
|
||||||
|
STAmount const toDebit (issue, 20);
|
||||||
|
{
|
||||||
|
// accountSend, no FT
|
||||||
|
LedgerEntrySet les (ledger, tapNONE);
|
||||||
|
|
||||||
|
expect (!les.areCreditsDeferred ());
|
||||||
|
|
||||||
|
STAmount const startingAmount =
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE);
|
||||||
|
|
||||||
|
les.accountSend (gw1Acc, aliceAcc, toCredit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount + toCredit);
|
||||||
|
|
||||||
|
les.accountSend (aliceAcc, gw1Acc, toDebit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount + toCredit - toDebit);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// rippleCredit, no FT
|
||||||
|
LedgerEntrySet les (ledger, tapNONE);
|
||||||
|
|
||||||
|
expect (!les.areCreditsDeferred ());
|
||||||
|
|
||||||
|
STAmount const startingAmount =
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE);
|
||||||
|
|
||||||
|
les.rippleCredit (gw1Acc, aliceAcc, toCredit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount + toCredit);
|
||||||
|
|
||||||
|
les.rippleCredit (aliceAcc, gw1Acc, toDebit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount + toCredit - toDebit);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// accountSend, w/ FT
|
||||||
|
LedgerEntrySet les (ledger, tapNONE);
|
||||||
|
les.enableDeferredCredits ();
|
||||||
|
expect (les.areCreditsDeferred ());
|
||||||
|
|
||||||
|
STAmount const startingAmount =
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE);
|
||||||
|
|
||||||
|
les.accountSend (gw1Acc, aliceAcc, toCredit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount);
|
||||||
|
|
||||||
|
les.accountSend (aliceAcc, gw1Acc, toDebit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount - toDebit);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// rippleCredit, w/ FT
|
||||||
|
LedgerEntrySet les (ledger, tapNONE);
|
||||||
|
les.enableDeferredCredits ();
|
||||||
|
expect (les.areCreditsDeferred ());
|
||||||
|
|
||||||
|
STAmount const startingAmount =
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE);
|
||||||
|
|
||||||
|
les.rippleCredit (gw1Acc, aliceAcc, toCredit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount);
|
||||||
|
|
||||||
|
les.rippleCredit (aliceAcc, gw1Acc, toDebit);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount - toDebit);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// rippleCredit, w/ FT & ScopedDeferCredits
|
||||||
|
LedgerEntrySet les (ledger, tapNONE);
|
||||||
|
|
||||||
|
STAmount const startingAmount =
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE);
|
||||||
|
{
|
||||||
|
ScopedDeferCredits g (les);
|
||||||
|
les.rippleCredit (gw1Acc, aliceAcc, toCredit);
|
||||||
|
expect (
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount);
|
||||||
|
}
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount + toCredit);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// issue_iou
|
||||||
|
LedgerEntrySet les (ledger, tapNONE);
|
||||||
|
STAmount const startingAmount =
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE);
|
||||||
|
les.enableDeferredCredits ();
|
||||||
|
expect (les.areCreditsDeferred ());
|
||||||
|
|
||||||
|
les.redeem_iou (aliceAcc, toDebit, issue);
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount - toDebit);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// redeem_iou
|
||||||
|
LedgerEntrySet les (ledger, tapNONE);
|
||||||
|
STAmount const startingAmount =
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE);
|
||||||
|
{
|
||||||
|
ScopedDeferCredits g (les);
|
||||||
|
expect (les.areCreditsDeferred ());
|
||||||
|
|
||||||
|
les.issue_iou (aliceAcc, toCredit, issue);
|
||||||
|
expect (
|
||||||
|
les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount);
|
||||||
|
}
|
||||||
|
expect (les.accountHolds (aliceAcc, usd, gw1Acc, fhIGNORE_FREEZE) ==
|
||||||
|
startingAmount + toCredit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void run ()
|
||||||
|
{
|
||||||
|
testSelfFunding ();
|
||||||
|
testSubtractCredits ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE (DeferredCredits, ledger, ripple);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // ripple
|
||||||
@@ -356,6 +356,23 @@ payWithPath(TestAccount& from, TestAccount const& to,
|
|||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STTx
|
||||||
|
payWithPath(TestAccount& from, TestAccount const& to,
|
||||||
|
std::string const& currency, std::string const& amount,
|
||||||
|
Ledger::pointer const& ledger, Json::Value const& path,
|
||||||
|
std::uint32_t flags, bool sign)
|
||||||
|
{
|
||||||
|
auto amountJson = Amount(std::stod(amount), currency, to).getJson();
|
||||||
|
Json::Value tx_json = getPaymentJson(from, to, amountJson);
|
||||||
|
|
||||||
|
tx_json[jss::Paths] = path;
|
||||||
|
tx_json[jss::Flags] = flags;
|
||||||
|
|
||||||
|
auto tx = parseTransaction(from, tx_json, sign);
|
||||||
|
applyTransaction(ledger, tx, sign);
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
createOffer(TestAccount& from, Amount const& in, Amount const& out,
|
createOffer(TestAccount& from, Amount const& in, Amount const& out,
|
||||||
@@ -369,6 +386,21 @@ createOffer(TestAccount& from, Amount const& in, Amount const& out,
|
|||||||
applyTransaction(ledger, tx, sign);
|
applyTransaction(ledger, tx, sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
createOfferWithFlags(TestAccount& from, Amount const& in, Amount const& out,
|
||||||
|
Ledger::pointer ledger, std::uint32_t flags,
|
||||||
|
bool sign)
|
||||||
|
{
|
||||||
|
Json::Value tx_json = getCommonTransactionJson(from);
|
||||||
|
tx_json[jss::TransactionType] = "OfferCreate";
|
||||||
|
tx_json[jss::TakerPays] = in.getJson();
|
||||||
|
tx_json[jss::TakerGets] = out.getJson();
|
||||||
|
tx_json[jss::Flags] = flags;
|
||||||
|
STTx tx = parseTransaction(from, tx_json, sign);
|
||||||
|
applyTransaction(ledger, tx, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// As currently implemented, this will cancel only the last offer made
|
// As currently implemented, this will cancel only the last offer made
|
||||||
// from this account.
|
// from this account.
|
||||||
void
|
void
|
||||||
@@ -496,5 +528,26 @@ verifyBalance(Ledger::pointer ledger, TestAccount const& account,
|
|||||||
"balance != amountReq");
|
"balance != amountReq");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json::Value pathNode (TestAccount const& acc)
|
||||||
|
{
|
||||||
|
Json::Value result;
|
||||||
|
result["account"] = acc.pk.humanAccountID();
|
||||||
|
result["type"] = 1;
|
||||||
|
result["type_hex"] = "0000000000000001";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value pathNode (OfferPathNode const& offer)
|
||||||
|
{
|
||||||
|
Json::Value result;
|
||||||
|
result["currency"] = offer.currency;
|
||||||
|
result["type"] = 48;
|
||||||
|
result["type_hex"] = "0000000000000030";
|
||||||
|
if (offer.issuer)
|
||||||
|
result["issuer"] = offer.issuer->pk.humanAccountID();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,10 +210,22 @@ payWithPath(TestAccount& from, TestAccount const& to,
|
|||||||
std::string const& currency, std::string const& amount,
|
std::string const& currency, std::string const& amount,
|
||||||
Ledger::pointer const& ledger, bool sign = true);
|
Ledger::pointer const& ledger, bool sign = true);
|
||||||
|
|
||||||
|
STTx
|
||||||
|
payWithPath(TestAccount& from, TestAccount const& to,
|
||||||
|
std::string const& currency, std::string const& amount,
|
||||||
|
Ledger::pointer const& ledger, Json::Value const& path,
|
||||||
|
std::uint32_t flags,
|
||||||
|
bool sign = true);
|
||||||
|
|
||||||
void
|
void
|
||||||
createOffer(TestAccount& from, Amount const& in, Amount const& out,
|
createOffer(TestAccount& from, Amount const& in, Amount const& out,
|
||||||
Ledger::pointer ledger, bool sign = true);
|
Ledger::pointer ledger, bool sign = true);
|
||||||
|
|
||||||
|
void
|
||||||
|
createOfferWithFlags(TestAccount& from, Amount const& in, Amount const& out,
|
||||||
|
Ledger::pointer ledger, std::uint32_t flags,
|
||||||
|
bool sign = true);
|
||||||
|
|
||||||
// As currently implemented, this will cancel only the last offer made
|
// As currently implemented, this will cancel only the last offer made
|
||||||
// from this account.
|
// from this account.
|
||||||
void
|
void
|
||||||
@@ -232,6 +244,42 @@ Json::Value findPath(Ledger::pointer ledger, TestAccount const& src,
|
|||||||
Amount const& dstAmount, beast::abstract_ostream& log,
|
Amount const& dstAmount, beast::abstract_ostream& log,
|
||||||
boost::optional<Json::Value> contextPaths = boost::none);
|
boost::optional<Json::Value> contextPaths = boost::none);
|
||||||
|
|
||||||
|
struct OfferPathNode
|
||||||
|
{
|
||||||
|
std::string currency;
|
||||||
|
boost::optional<TestAccount> issuer;
|
||||||
|
OfferPathNode(std::string s, TestAccount const& iss)
|
||||||
|
:currency(std::move(s)), issuer(iss) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Json::Value pathNode (TestAccount const& acc);
|
||||||
|
|
||||||
|
Json::Value pathNode (OfferPathNode const& offer);
|
||||||
|
|
||||||
|
inline void createPathHelper (Json::Value& result)
|
||||||
|
{
|
||||||
|
// base case
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class First, class... Rest>
|
||||||
|
void createPathHelper (Json::Value& result,
|
||||||
|
First&& first,
|
||||||
|
Rest&&... rest)
|
||||||
|
{
|
||||||
|
result.append (pathNode (std::forward<First> (first)));
|
||||||
|
createPathHelper(result, rest...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class First, class... Rest>
|
||||||
|
Json::Value createPath (First&& first,
|
||||||
|
Rest&&... rest)
|
||||||
|
{
|
||||||
|
Json::Value result;
|
||||||
|
createPathHelper (result, first, rest...);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SLE::pointer
|
SLE::pointer
|
||||||
get_ledger_entry_ripple_state(Ledger::pointer ledger,
|
get_ledger_entry_ripple_state(Ledger::pointer ledger,
|
||||||
RippleAddress account1, RippleAddress account2,
|
RippleAddress account1, RippleAddress account2,
|
||||||
|
|||||||
@@ -292,14 +292,19 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto rc = path::RippleCalc::rippleCalculate (
|
|
||||||
mEngine->view (),
|
path::RippleCalc::Output rc;
|
||||||
maxSourceAmount,
|
{
|
||||||
saDstAmount,
|
ScopedDeferCredits g (mEngine->view ());
|
||||||
uDstAccountID,
|
rc = path::RippleCalc::rippleCalculate (
|
||||||
mTxnAccountID,
|
mEngine->view (),
|
||||||
spsPaths,
|
maxSourceAmount,
|
||||||
&rcInput);
|
saDstAmount,
|
||||||
|
uDstAccountID,
|
||||||
|
mTxnAccountID,
|
||||||
|
spsPaths,
|
||||||
|
&rcInput);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: is this right? If the amount is the correct amount, was
|
// TODO: is this right? If the amount is the correct amount, was
|
||||||
// the delivered amount previously set?
|
// the delivered amount previously set?
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <ripple/app/ledger/AcceptedLedger.cpp>
|
#include <ripple/app/ledger/AcceptedLedger.cpp>
|
||||||
#include <ripple/app/ledger/DirectoryEntryIterator.cpp>
|
#include <ripple/app/ledger/DirectoryEntryIterator.cpp>
|
||||||
#include <ripple/app/ledger/OrderBookIterator.cpp>
|
#include <ripple/app/ledger/OrderBookIterator.cpp>
|
||||||
|
#include <ripple/app/ledger/DeferredCredits.cpp>
|
||||||
#include <ripple/app/consensus/DisputedTx.cpp>
|
#include <ripple/app/consensus/DisputedTx.cpp>
|
||||||
#include <ripple/app/misc/HashRouter.cpp>
|
#include <ripple/app/misc/HashRouter.cpp>
|
||||||
#include <ripple/app/paths/AccountCurrencies.cpp>
|
#include <ripple/app/paths/AccountCurrencies.cpp>
|
||||||
@@ -31,3 +32,4 @@
|
|||||||
#include <ripple/app/paths/Pathfinder.cpp>
|
#include <ripple/app/paths/Pathfinder.cpp>
|
||||||
#include <ripple/app/misc/AmendmentTableImpl.cpp>
|
#include <ripple/app/misc/AmendmentTableImpl.cpp>
|
||||||
#include <ripple/app/misc/tests/AmendmentTable.test.cpp>
|
#include <ripple/app/misc/tests/AmendmentTable.test.cpp>
|
||||||
|
#include <ripple/app/ledger/tests/DeferredCredits.test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user