Better types and more comments in RippleCalc.

* Better automatic conversions to and from tagged uint160 varints.
* Start using tagged variants of uint160 for Currency, Account.
* Comments from 2014/6/11 RippleCalc session.
This commit is contained in:
Tom Ritchford
2014-06-10 18:18:03 -04:00
committed by Vinnie Falco
parent a23013abc1
commit e24cba8c35
42 changed files with 1849 additions and 1310 deletions

View File

@@ -1835,6 +1835,8 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\basics\utility\PlatformMacros.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\basics\utility\StringConcat.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\basics\utility\StringUtilities.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -1855,6 +1857,8 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\basics\utility\Time.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\basics\utility\ToString.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\basics\utility\UptimeTimer.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -1995,6 +1999,9 @@
<ClCompile Include="..\..\src\ripple\module\app\book\impl\Taker.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\book\impl\Types.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\book\tests\OfferStream.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -2267,15 +2274,6 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\node\SqliteFactory.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNode.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeAccountFwd.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeAccountRev.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeAdvance.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -2285,16 +2283,25 @@
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeDeliverRev.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeOffer.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeRipple.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\paths\CalcState.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\Calculators.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeAccountLiquidityForward.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeAccountLiquidityReverse.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeLiquidity.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeOfferLiquidity.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeRippleLiquidity.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\Node.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -2342,6 +2349,9 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\Tuning.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\Types.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\paths\Types.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\peers\ClusterNodeStatus.h">

View File

@@ -2781,6 +2781,9 @@
<ClInclude Include="..\..\src\ripple\basics\utility\PlatformMacros.h">
<Filter>ripple\basics\utility</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\basics\utility\StringConcat.h">
<Filter>ripple\basics\utility</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\basics\utility\StringUtilities.cpp">
<Filter>ripple\basics\utility</Filter>
</ClCompile>
@@ -2805,6 +2808,9 @@
<ClInclude Include="..\..\src\ripple\basics\utility\Time.h">
<Filter>ripple\basics\utility</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\basics\utility\ToString.h">
<Filter>ripple\basics\utility</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\basics\utility\UptimeTimer.cpp">
<Filter>ripple\basics\utility</Filter>
</ClCompile>
@@ -2985,6 +2991,9 @@
<ClCompile Include="..\..\src\ripple\module\app\book\impl\Taker.cpp">
<Filter>ripple\module\app\book\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\book\impl\Types.cpp">
<Filter>ripple\module\app\book\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\book\tests\OfferStream.test.cpp">
<Filter>ripple\module\app\book\tests</Filter>
</ClCompile>
@@ -3312,15 +3321,6 @@
<ClInclude Include="..\..\src\ripple\module\app\node\SqliteFactory.h">
<Filter>ripple\module\app\node</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNode.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeAccountFwd.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeAccountRev.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeAdvance.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
@@ -3330,18 +3330,27 @@
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeDeliverRev.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeOffer.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\CalcNodeRipple.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\paths\CalcState.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\Calculators.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeAccountLiquidityForward.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeAccountLiquidityReverse.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeLiquidity.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeOfferLiquidity.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\ComputeRippleLiquidity.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\app\paths\Node.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
@@ -3399,6 +3408,9 @@
<ClInclude Include="..\..\src\ripple\module\app\paths\Tuning.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\Types.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\paths\Types.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>

View File

@@ -0,0 +1,77 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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_STRINGCONCAT_H
#define RIPPLE_STRINGCONCAT_H
#include <ripple/basics/utility/ToString.h>
namespace ripple {
namespace detail {
// ConcatArg is used to represent arguments to stringConcat.
struct ConcatArg {
ConcatArg(std::string const& s) : data_(s.data()), size_(s.size())
{
}
ConcatArg(char const* s) : data_(s), size_(strlen(s))
{
}
template <typename T>
ConcatArg(T t) : string_(to_string(t)),
data_(string_.data()),
size_(string_.size())
{
}
std::string string_;
char const* data_;
std::size_t size_;
};
} // namespace detail
/** Concatenate strings, numbers, bools and chars into one string in O(n) time.
Usage:
stringConcat({"hello ", 23, 'x', true});
Returns:
"hello 23xtrue"
*/
inline std::string stringConcat(std::vector<detail::ConcatArg> args)
{
int capacity = 0;
for (auto const& a: args)
capacity += a.size_;
std::string result;
result.reserve(capacity);
for (auto const& a: args)
result.append(a.data_, a.data_ + a.size_);
return result;
}
} // ripple
#endif

View File

@@ -23,6 +23,9 @@
#include <boost/regex.hpp>
#include <cstdarg>
#include <ripple/basics/utility/ToString.h>
#include <ripple/basics/utility/StringConcat.h>
namespace ripple {
// VFALCO TODO Replace these with something more robust and without macros.
@@ -415,7 +418,7 @@ public:
void testToString ()
{
testcase ("toString");
auto result = toString("hello");
auto result = to_string("hello");
expect(result == "hello", result);
}

View File

@@ -139,88 +139,6 @@ extern beast::StringPairArray
parseDelimitedKeyValueString (
beast::String s, beast::beast_wchar delimiter='|');
/** toString() generalizes std::to_string to handle bools, chars, and strings.
It's also possible to provide implementation of toString for a class
which needs a string implementation.
*/
template <class T>
typename std::enable_if<std::is_arithmetic<T>::value,
std::string>::type
toString(T t)
{
return std::to_string(t);
}
inline std::string toString(bool b)
{
return b ? "true" : "false";
}
inline std::string toString(char c)
{
return std::string(1, c);
}
inline std::string toString(std::string s)
{
return s;
}
inline std::string toString(char const* s)
{
return s;
}
namespace detail {
// ConcatArg is used to represent arguments to stringConcat.
struct ConcatArg {
ConcatArg(std::string const& s) : data_(s.data()), size_(s.size())
{
}
ConcatArg(char const* s) : data_(s), size_(strlen(s))
{
}
template <typename T>
ConcatArg(T t) : string_(toString(t)),
data_(string_.data()),
size_(string_.size())
{
}
std::string string_;
char const* data_;
std::size_t size_;
};
} // namespace detail
/** Concatenate strings, numbers, bools and chars into one string in O(n) time.
Usage:
stringConcat({"hello ", 23, 'x', true});
Returns:
"hello 23xtrue"
*/
inline std::string stringConcat(std::vector<detail::ConcatArg> args)
{
int capacity = 0;
for (auto const& a: args)
capacity += a.size_;
std::string result;
result.reserve(capacity);
for (auto const& a: args)
result.append(a.data_, a.data_ + a.size_);
return result;
}
} // ripple
#endif

View File

@@ -0,0 +1,61 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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_TOSTRING_H
#define RIPPLE_TOSTRING_H
namespace ripple {
/** to_string() generalizes std::to_string to handle bools, chars, and strings.
It's also possible to provide implementation of to_string for a class
which needs a string implementation.
*/
template <class T>
typename std::enable_if<std::is_arithmetic<T>::value,
std::string>::type
to_string(T t)
{
return std::to_string(t);
}
inline std::string to_string(bool b)
{
return b ? "true" : "false";
}
inline std::string to_string(char c)
{
return std::string(1, c);
}
inline std::string to_string(std::string s)
{
return s;
}
inline std::string to_string(char const* s)
{
return s;
}
} // ripple
#endif

View File

@@ -30,15 +30,42 @@
namespace ripple {
namespace core {
namespace detail {
class AccountTag {};
class CurrencyTag {};
} // detail
typedef base_uint<160, detail::AccountTag> Account;
typedef base_uint<160, detail::CurrencyTag> Currency;
inline std::string to_string(Currency const& c)
{
return STAmount::createHumanCurrency(c);
}
inline std::string to_string(Account const& a)
{
return RippleAddress::createHumanAccountID(a);
}
inline std::ostream& operator<< (std::ostream& os, Account const& x)
{
os << to_string (x);
return os;
}
inline std::ostream& operator<< (std::ostream& os, Currency const& x)
{
os << to_string (x);
return os;
}
/** A mutable view that overlays an immutable ledger to track changes. */
typedef LedgerEntrySet LedgerView;
/** Identifies an account. */
typedef uint160 Account;
/** Asset identifiers. */
typedef RippleCurrency Currency;
typedef RippleIssuer Issuer;
typedef RippleAsset Asset;
typedef RippleAssetRef AssetRef;
@@ -57,7 +84,13 @@ public:
typedef std::chrono::seconds duration;
};
}
} // core
inline bool isXRP(core::Currency const& c)
{
return c == zero;
}
} // ripple
#endif

View File

@@ -0,0 +1,29 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2014 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/module/app/book/Types.h>
namespace ripple {
namespace core {
const path::Account XRP_ACCOUNT(0);
const path::Currency XRP_CURRENCY(0);
}
}

View File

@@ -1535,12 +1535,12 @@ uint256 Ledger::getQualityNext (uint256 const& uBase)
return uResult;
}
uint256 Ledger::getAccountRootIndex (const uint160& uAccountID)
uint256 Ledger::getAccountRootIndex (const uint160& account)
{
Serializer s (22);
s.add16 (spaceAccount); // 2
s.add160 (uAccountID); // 20
s.add160 (account); // 20
return s.getSHA512Half ();
}
@@ -1782,23 +1782,23 @@ uint256 Ledger::getNicknameIndex (uint256 const& uNickname)
return s.getSHA512Half ();
}
uint256 Ledger::getOfferIndex (const uint160& uAccountID, std::uint32_t uSequence)
uint256 Ledger::getOfferIndex (const uint160& account, std::uint32_t uSequence)
{
Serializer s (26);
s.add16 (spaceOffer); // 2
s.add160 (uAccountID); // 20
s.add160 (account); // 20
s.add32 (uSequence); // 4
return s.getSHA512Half ();
}
uint256 Ledger::getOwnerDirIndex (const uint160& uAccountID)
uint256 Ledger::getOwnerDirIndex (const uint160& account)
{
Serializer s (22);
s.add16 (spaceOwnerDir); // 2
s.add160 (uAccountID); // 20
s.add160 (account); // 20
return s.getSHA512Half ();
}

View File

@@ -317,7 +317,7 @@ public:
std::vector<uint256> getNeededAccountStateHashes (int max, SHAMapSyncFilter * filter);
// index calculation functions
static uint256 getAccountRootIndex (const uint160 & uAccountID);
static uint256 getAccountRootIndex (const uint160 & account);
static uint256 getAccountRootIndex (const RippleAddress & account)
{
@@ -373,13 +373,13 @@ public:
SLE::pointer getOffer (uint256 const & uIndex);
SLE::pointer getOffer (const uint160 & uAccountID, std::uint32_t uSequence)
SLE::pointer getOffer (const uint160 & account, std::uint32_t uSequence)
{
return getOffer (getOfferIndex (uAccountID, uSequence));
return getOffer (getOfferIndex (account, uSequence));
}
// The index of an offer.
static uint256 getOfferIndex (const uint160 & uAccountID, std::uint32_t uSequence);
static uint256 getOfferIndex (const uint160 & account, std::uint32_t uSequence);
//
// Owner functions
@@ -389,7 +389,7 @@ public:
// into a 256 bit object (I think....need to research this)
//
// All items controlled by an account are here: offers
static uint256 getOwnerDirIndex (const uint160 & uAccountID);
static uint256 getOwnerDirIndex (const uint160 & account);
//
// Directory functions

View File

@@ -356,7 +356,7 @@ Json::Value LedgerEntrySet::getJson (int) const
}
SLE::pointer LedgerEntrySet::getForMod (uint256 const& node, Ledger::ref ledger,
ripple::unordered_map<uint256, SLE::pointer>& newMods)
NodeToLedgerEntry& newMods)
{
auto it = mEntries.find (node);
@@ -380,7 +380,7 @@ SLE::pointer LedgerEntrySet::getForMod (uint256 const& node, Ledger::ref ledger,
return it->second.mEntry;
}
auto me (newMods.find (node));
auto me = newMods.find (node);
if (me != newMods.end ())
{
@@ -398,7 +398,7 @@ SLE::pointer LedgerEntrySet::getForMod (uint256 const& node, Ledger::ref ledger,
}
bool LedgerEntrySet::threadTx (const RippleAddress& threadTo, Ledger::ref ledger,
ripple::unordered_map<uint256, SLE::pointer>& newMods)
NodeToLedgerEntry& newMods)
{
#ifdef META_DEBUG
WriteLog (lsTRACE, LedgerEntrySet) << "Thread to " << threadTo.getAccountID ();
@@ -416,7 +416,7 @@ bool LedgerEntrySet::threadTx (const RippleAddress& threadTo, Ledger::ref ledger
}
bool LedgerEntrySet::threadTx (SLE::ref threadTo, Ledger::ref ledger,
ripple::unordered_map<uint256, SLE::pointer>& newMods)
NodeToLedgerEntry& newMods)
{
// node = the node that was modified/deleted/created
// threadTo = the node that needs to know
@@ -435,7 +435,7 @@ bool LedgerEntrySet::threadTx (SLE::ref threadTo, Ledger::ref ledger,
}
bool LedgerEntrySet::threadOwners (SLE::ref node, Ledger::ref ledger,
ripple::unordered_map<uint256, SLE::pointer>& newMods)
NodeToLedgerEntry& newMods)
{
// thread new or modified node to owner or owners
if (node->hasOneOwner ()) // thread to owner's account
@@ -463,7 +463,7 @@ void LedgerEntrySet::calcRawMeta (Serializer& s, TER result, std::uint32_t index
// calculate the raw meta data and return it. This must be called before the set is committed
// Entries modified only as a result of building the transaction metadata
ripple::unordered_map<uint256, SLE::pointer> newMod;
NodeToLedgerEntry newMod;
for (auto& it : mEntries)
{
@@ -986,6 +986,7 @@ bool LedgerEntrySet::dirNext (
}
sleNode = sleNext;
// TODO(tom): make this iterative.
return dirNext (uRootIndex, sleNode, uDirEntry, uEntryIndex);
}
}
@@ -1063,13 +1064,18 @@ TER LedgerEntrySet::offerDelete (SLE::pointer sleOffer)
uint256 offerIndex = sleOffer->getIndex ();
uint160 uOwnerID = sleOffer->getFieldAccount160 (sfAccount);
bool bOwnerNode = sleOffer->isFieldPresent (sfOwnerNode); // Detect legacy dirs.
// Detect legacy directories.
bool bOwnerNode = sleOffer->isFieldPresent (sfOwnerNode);
std::uint64_t uOwnerNode = sleOffer->getFieldU64 (sfOwnerNode);
uint256 uDirectory = sleOffer->getFieldH256 (sfBookDirectory);
std::uint64_t uBookNode = sleOffer->getFieldU64 (sfBookNode);
TER terResult = dirDelete (false, uOwnerNode, Ledger::getOwnerDirIndex (uOwnerID), offerIndex, false, !bOwnerNode);
TER terResult2 = dirDelete (false, uBookNode, uDirectory, offerIndex, true, false);
TER terResult = dirDelete (
false, uOwnerNode,
Ledger::getOwnerDirIndex (uOwnerID), offerIndex, false, !bOwnerNode);
TER terResult2 = dirDelete (
false, uBookNode, uDirectory, offerIndex, true, false);
if (tesSUCCESS == terResult)
ownerCountAdjust (uOwnerID, -1);
@@ -1079,9 +1085,9 @@ TER LedgerEntrySet::offerDelete (SLE::pointer sleOffer)
return (terResult == tesSUCCESS) ? terResult2 : terResult;
}
TER LedgerEntrySet::offerDelete (uint256 const& uOfferIndex)
TER LedgerEntrySet::offerDelete (uint256 const& offerIndex)
{
SLE::pointer sleOffer = entryCache (ltOFFER, uOfferIndex);
SLE::pointer sleOffer = entryCache (ltOFFER, offerIndex);
if (!sleOffer)
return tesSUCCESS;
@@ -1090,11 +1096,18 @@ TER LedgerEntrySet::offerDelete (uint256 const& uOfferIndex)
}
// Returns amount owed by uToAccountID to uFromAccountID.
// <-- $owed/uCurrencyID/uToAccountID: positive: uFromAccountID holds IOUs., negative: uFromAccountID owes IOUs.
STAmount LedgerEntrySet::rippleOwed (const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
// <-- $owed/currency/uToAccountID:
// positive: uFromAccountID holds IOUs.,
// negative: uFromAccountID owes IOUs.
STAmount LedgerEntrySet::rippleOwed (
const uint160& uToAccountID, const uint160& uFromAccountID,
const uint160& currency)
{
STAmount saBalance;
SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (uToAccountID, uFromAccountID, uCurrencyID));
STAmount saBalance;
SLE::pointer sleRippleState = entryCache (
ltRIPPLE_STATE,
Ledger::getRippleStateIndex (
uToAccountID, uFromAccountID, currency));
if (sleRippleState)
{
@@ -1107,52 +1120,49 @@ STAmount LedgerEntrySet::rippleOwed (const uint160& uToAccountID, const uint160&
}
else
{
saBalance.clear (uCurrencyID, uToAccountID);
saBalance.clear (currency, uToAccountID);
WriteLog (lsDEBUG, LedgerEntrySet) << "rippleOwed:" <<
" No credit line between " <<
RippleAddress::createHumanAccountID (uFromAccountID) <<
" and " << RippleAddress::createHumanAccountID (uToAccountID) <<
" for " << STAmount::createHumanCurrency (uCurrencyID);
#if 0
// We could cut off coming here if we test for no line sooner.
assert (false);
#endif
" for " << STAmount::createHumanCurrency (currency);
}
return saBalance;
}
// Maximum amount of IOUs uToAccountID will hold from uFromAccountID.
// <-- $amount/uCurrencyID/uToAccountID.
STAmount LedgerEntrySet::rippleLimit (const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
// <-- $amount/currency/uToAccountID.
STAmount LedgerEntrySet::rippleLimit (
const uint160& uToAccountID, const uint160& uFromAccountID,
const uint160& currency)
{
STAmount saLimit;
SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (uToAccountID, uFromAccountID, uCurrencyID));
STAmount saLimit;
auto sleRippleState = entryCache (
ltRIPPLE_STATE,
Ledger::getRippleStateIndex (
uToAccountID, uFromAccountID, currency));
if (sleRippleState)
{
saLimit = sleRippleState->getFieldAmount (uToAccountID < uFromAccountID ? sfLowLimit : sfHighLimit);
saLimit = sleRippleState->getFieldAmount (
uToAccountID < uFromAccountID ? sfLowLimit : sfHighLimit);
saLimit.setIssuer (uToAccountID);
}
else
{
saLimit.clear (uCurrencyID, uToAccountID);
#if 0
// We could cut off coming here if we test for no line sooner.
assert (false);
#endif
saLimit.clear (currency, uToAccountID);
}
return saLimit;
}
std::uint32_t LedgerEntrySet::rippleTransferRate (const uint160& uIssuerID)
std::uint32_t LedgerEntrySet::rippleTransferRate (const uint160& issuer)
{
SLE::pointer sleAccount (entryCache (
ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uIssuerID)));
ltACCOUNT_ROOT, Ledger::getAccountRootIndex (issuer)));
std::uint32_t uQuality =
sleAccount && sleAccount->isFieldPresent (sfTransferRate)
@@ -1160,7 +1170,7 @@ std::uint32_t LedgerEntrySet::rippleTransferRate (const uint160& uIssuerID)
: QUALITY_ONE;
WriteLog (lsTRACE, LedgerEntrySet) << "rippleTransferRate:" <<
" uIssuerID=" << RippleAddress::createHumanAccountID (uIssuerID) <<
" issuer=" << RippleAddress::createHumanAccountID (issuer) <<
" account_exists=" << std::boolalpha << !!sleAccount <<
" transfer_rate=" << (uQuality / 1000000000.0);
@@ -1170,21 +1180,21 @@ std::uint32_t LedgerEntrySet::rippleTransferRate (const uint160& uIssuerID)
std::uint32_t
LedgerEntrySet::rippleTransferRate (const uint160& uSenderID,
const uint160& uReceiverID,
const uint160& uIssuerID)
const uint160& issuer)
{
// If calculating the transfer rate from or to the issuer of the currency
// no fees are assessed.
return uSenderID == uIssuerID || uReceiverID == uIssuerID
return uSenderID == issuer || uReceiverID == issuer
? QUALITY_ONE
: rippleTransferRate (uIssuerID);
: rippleTransferRate (issuer);
}
// XXX Might not need this, might store in nodes on calc reverse.
std::uint32_t
LedgerEntrySet::rippleQualityIn (const uint160& uToAccountID,
const uint160& uFromAccountID,
const uint160& uCurrencyID, SField::ref sfLow,
SField::ref sfHigh)
const uint160& uCurrencyID,
SField::ref sfLow, SField::ref sfHigh)
{
std::uint32_t uQuality (QUALITY_ONE);
@@ -1210,7 +1220,7 @@ LedgerEntrySet::rippleQualityIn (const uint160& uToAccountID,
// XXX Ideally, catch no before this. So we can assert to be stricter.
uQuality = QUALITY_ONE;
}
WriteLog (lsTRACE, LedgerEntrySet) << "rippleQuality: " <<
(sfLow == sfLowQualityIn ? "in" : "out") <<
" uToAccountID=" << RippleAddress::createHumanAccountID (uToAccountID) <<
@@ -1222,30 +1232,30 @@ LedgerEntrySet::rippleQualityIn (const uint160& uToAccountID,
return uQuality;
}
// Return how much of uIssuerID's uCurrencyID IOUs that uAccountID holds. May be negative.
// <-- IOU's uAccountID has of uIssuerID.
STAmount LedgerEntrySet::rippleHolds (const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
// Return how much of issuer's currency IOUs that account holds. May be negative.
// <-- IOU's account has of issuer.
STAmount LedgerEntrySet::rippleHolds (const uint160& account, const uint160& currency, const uint160& issuer)
{
STAmount saBalance;
SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE,
Ledger::getRippleStateIndex (uAccountID, uIssuerID, uCurrencyID));
Ledger::getRippleStateIndex (account, issuer, currency));
if (!sleRippleState)
{
saBalance.clear (uCurrencyID, uIssuerID);
saBalance.clear (currency, issuer);
}
else if (uAccountID > uIssuerID)
else if (account > issuer)
{
saBalance = sleRippleState->getFieldAmount (sfBalance);
saBalance.negate (); // Put balance in uAccountID terms.
saBalance.negate (); // Put balance in account terms.
saBalance.setIssuer (uIssuerID);
saBalance.setIssuer (issuer);
}
else
{
saBalance = sleRippleState->getFieldAmount (sfBalance);
saBalance.setIssuer (uIssuerID);
saBalance.setIssuer (issuer);
}
return saBalance;
@@ -1253,16 +1263,19 @@ STAmount LedgerEntrySet::rippleHolds (const uint160& uAccountID, const uint160&
// Returns the amount an account can spend without going into debt.
//
// <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative.
STAmount LedgerEntrySet::accountHolds (const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
// <-- saAmount: amount of currency held by account. May be negative.
STAmount LedgerEntrySet::accountHolds (
const uint160& account, const uint160& currency,
const uint160& issuer)
{
STAmount saAmount;
if (!uCurrencyID)
if (!currency)
{
SLE::pointer sleAccount = entryCache (ltACCOUNT_ROOT,
Ledger::getAccountRootIndex (uAccountID));
std::uint64_t uReserve = mLedger->getReserve (sleAccount->getFieldU32 (sfOwnerCount));
Ledger::getAccountRootIndex (account));
std::uint64_t uReserve = mLedger->getReserve (
sleAccount->getFieldU32 (sfOwnerCount));
STAmount saBalance = sleAccount->getFieldAmount (sfBalance);
@@ -1276,48 +1289,52 @@ STAmount LedgerEntrySet::accountHolds (const uint160& uAccountID, const uint160&
}
WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" <<
" uAccountID=" << RippleAddress::createHumanAccountID (uAccountID) <<
" account=" << RippleAddress::createHumanAccountID (account) <<
" saAmount=" << saAmount.getFullText () <<
" saBalance=" << saBalance.getFullText () <<
" uReserve=" << uReserve;
}
else
{
saAmount = rippleHolds (uAccountID, uCurrencyID, uIssuerID);
saAmount = rippleHolds (account, currency, issuer);
WriteLog (lsTRACE, LedgerEntrySet) << "accountHolds:" <<
" uAccountID=" << RippleAddress::createHumanAccountID (uAccountID) <<
" account=" << RippleAddress::createHumanAccountID (account) <<
" saAmount=" << saAmount.getFullText ();
}
return saAmount;
}
// Returns the funds available for uAccountID for a currency/issuer.
// Use when you need a default for rippling uAccountID's currency.
// 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?
// --> saDefault/currency/issuer
// <-- saFunds: Funds available. May be negative.
// If the issuer is the same as uAccountID, funds are unlimited, use result is saDefault.
STAmount LedgerEntrySet::accountFunds (const uint160& uAccountID, const STAmount& saDefault)
//
// If the issuer is the same as account, funds are unlimited, use result is
// saDefault.
STAmount LedgerEntrySet::accountFunds (
const uint160& account, const STAmount& saDefault)
{
STAmount saFunds;
if (!saDefault.isNative () && saDefault.getIssuer () == uAccountID)
if (!saDefault.isNative () && saDefault.getIssuer () == account)
{
saFunds = saDefault;
WriteLog (lsTRACE, LedgerEntrySet) << "accountFunds:" <<
" uAccountID=" << RippleAddress::createHumanAccountID (uAccountID) <<
" account=" << RippleAddress::createHumanAccountID (account) <<
" saDefault=" << saDefault.getFullText () <<
" SELF-FUNDED";
}
else
{
saFunds = accountHolds (uAccountID, saDefault.getCurrency (), saDefault.getIssuer ());
saFunds = accountHolds (
account, saDefault.getCurrency (), saDefault.getIssuer ());
WriteLog (lsTRACE, LedgerEntrySet) << "accountFunds:" <<
" uAccountID=" << RippleAddress::createHumanAccountID (uAccountID) <<
" account=" << RippleAddress::createHumanAccountID (account) <<
" saDefault=" << saDefault.getFullText () <<
" saFunds=" << saFunds.getFullText ();
}
@@ -1329,12 +1346,12 @@ STAmount LedgerEntrySet::accountFunds (const uint160& uAccountID, const STAmount
STAmount LedgerEntrySet::rippleTransferFee (
const uint160& uSenderID,
const uint160& uReceiverID,
const uint160& uIssuerID,
const uint160& issuer,
const STAmount& saAmount)
{
if (uSenderID != uIssuerID && uReceiverID != uIssuerID)
if (uSenderID != issuer && uReceiverID != issuer)
{
std::uint32_t uTransitRate = rippleTransferRate (uIssuerID);
std::uint32_t uTransitRate = rippleTransferRate (issuer);
if (QUALITY_ONE != uTransitRate)
{
@@ -1366,8 +1383,10 @@ TER LedgerEntrySet::trustCreate (
SLE::ref sleAccount, // --> the account being set.
const bool bAuth, // --> authorize account.
const bool bNoRipple, // --> others cannot ripple through
const STAmount& saBalance, // --> balance of account being set. Issuer should be ACCOUNT_ONE
const STAmount& saLimit, // --> limit for account being set. Issuer should be the account being set.
const STAmount& saBalance, // --> balance of account being set.
// Issuer should be ACCOUNT_ONE
const STAmount& saLimit, // --> limit for account being set.
// Issuer should be the account being set.
const std::uint32_t uQualityIn,
const std::uint32_t uQualityOut)
{
@@ -1403,17 +1422,24 @@ TER LedgerEntrySet::trustCreate (
const bool bSetDst = saLimit.getIssuer () == uDstAccountID;
const bool bSetHigh = bSrcHigh ^ bSetDst;
sleRippleState->setFieldU64 (sfLowNode, uLowNode); // Remember deletion hints.
// Remember deletion hints.
sleRippleState->setFieldU64 (sfLowNode, uLowNode);
sleRippleState->setFieldU64 (sfHighNode, uHighNode);
sleRippleState->setFieldAmount (!bSetHigh ? sfLowLimit : sfHighLimit, saLimit);
sleRippleState->setFieldAmount ( bSetHigh ? sfLowLimit : sfHighLimit, STAmount (saBalance.getCurrency (), bSetDst ? uSrcAccountID : uDstAccountID));
sleRippleState->setFieldAmount (
!bSetHigh ? sfLowLimit : sfHighLimit, saLimit);
sleRippleState->setFieldAmount (
bSetHigh ? sfLowLimit : sfHighLimit,
STAmount (saBalance.getCurrency (),
bSetDst ? uSrcAccountID : uDstAccountID));
if (uQualityIn)
sleRippleState->setFieldU32 (!bSetHigh ? sfLowQualityIn : sfHighQualityIn, uQualityIn);
sleRippleState->setFieldU32 (
!bSetHigh ? sfLowQualityIn : sfHighQualityIn, uQualityIn);
if (uQualityOut)
sleRippleState->setFieldU32 (!bSetHigh ? sfLowQualityOut : sfHighQualityOut, uQualityOut);
sleRippleState->setFieldU32 (
!bSetHigh ? sfLowQualityOut : sfHighQualityOut, uQualityOut);
std::uint32_t uFlags = !bSetHigh ? lsfLowReserve : lsfHighReserve;
@@ -1427,30 +1453,49 @@ TER LedgerEntrySet::trustCreate (
}
sleRippleState->setFieldU32 (sfFlags, uFlags);
ownerCountAdjust (
!bSetDst ? uSrcAccountID : uDstAccountID, 1, sleAccount);
ownerCountAdjust (!bSetDst ? uSrcAccountID : uDstAccountID, 1, sleAccount);
sleRippleState->setFieldAmount (sfBalance, bSetHigh ? -saBalance : saBalance); // ONLY: Create ripple balance.
// ONLY: Create ripple balance.
sleRippleState->setFieldAmount (
sfBalance, bSetHigh ? -saBalance : saBalance);
}
return terResult;
}
TER LedgerEntrySet::trustDelete (SLE::ref sleRippleState, const uint160& uLowAccountID, const uint160& uHighAccountID)
TER LedgerEntrySet::trustDelete (
SLE::ref sleRippleState, const uint160& uLowAccountID,
const uint160& uHighAccountID)
{
bool bLowNode = sleRippleState->isFieldPresent (sfLowNode); // Detect legacy dirs.
// Detect legacy dirs.
bool bLowNode = sleRippleState->isFieldPresent (sfLowNode);
bool bHighNode = sleRippleState->isFieldPresent (sfHighNode);
std::uint64_t uLowNode = sleRippleState->getFieldU64 (sfLowNode);
std::uint64_t uHighNode = sleRippleState->getFieldU64 (sfHighNode);
TER terResult;
WriteLog (lsTRACE, LedgerEntrySet) << "trustDelete: Deleting ripple line: low";
terResult = dirDelete (false, uLowNode, Ledger::getOwnerDirIndex (uLowAccountID), sleRippleState->getIndex (), false, !bLowNode);
WriteLog (lsTRACE, LedgerEntrySet)
<< "trustDelete: Deleting ripple line: low";
terResult = dirDelete (
false,
uLowNode,
Ledger::getOwnerDirIndex (uLowAccountID),
sleRippleState->getIndex (),
false,
!bLowNode);
if (tesSUCCESS == terResult)
{
WriteLog (lsTRACE, LedgerEntrySet) << "trustDelete: Deleting ripple line: high";
terResult = dirDelete (false, uHighNode, Ledger::getOwnerDirIndex (uHighAccountID), sleRippleState->getIndex (), false, !bHighNode);
WriteLog (lsTRACE, LedgerEntrySet)
<< "trustDelete: Deleting ripple line: high";
terResult = dirDelete (
false,
uHighNode,
Ledger::getOwnerDirIndex (uHighAccountID),
sleRippleState->getIndex (),
false,
!bHighNode);
}
WriteLog (lsTRACE, LedgerEntrySet) << "trustDelete: Deleting ripple line: state";
@@ -1463,29 +1508,33 @@ TER LedgerEntrySet::trustDelete (SLE::ref sleRippleState, const uint160& uLowAcc
// - Redeeming IOUs and/or sending sender's own IOUs.
// - Create trust line of needed.
// --> bCheckIssuer : normally require issuer to be involved.
TER LedgerEntrySet::rippleCredit (const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer)
TER LedgerEntrySet::rippleCredit (
const uint160& uSenderID, const uint160& uReceiverID,
const STAmount& saAmount, bool bCheckIssuer)
{
uint160 uIssuerID = saAmount.getIssuer ();
uint160 uCurrencyID = saAmount.getCurrency ();
uint160 issuer = saAmount.getIssuer ();
uint160 currency = saAmount.getCurrency ();
// Make sure issuer is involved.
assert (!bCheckIssuer || uSenderID == uIssuerID || uReceiverID == uIssuerID);
assert (
!bCheckIssuer || uSenderID == issuer || uReceiverID == issuer);
// Disallow sending to self.
assert (uSenderID != uReceiverID);
bool bSenderHigh = uSenderID > uReceiverID;
uint256 uIndex = Ledger::getRippleStateIndex (uSenderID, uReceiverID, saAmount.getCurrency ());
SLE::pointer sleRippleState = entryCache (ltRIPPLE_STATE, uIndex);
bool bSenderHigh = uSenderID > uReceiverID;
uint256 uIndex = Ledger::getRippleStateIndex (
uSenderID, uReceiverID, saAmount.getCurrency ());
auto sleRippleState = entryCache (ltRIPPLE_STATE, uIndex);
TER terResult;
TER terResult;
assert (!!uSenderID && uSenderID != ACCOUNT_ONE);
assert (!!uReceiverID && uReceiverID != ACCOUNT_ONE);
if (!sleRippleState)
{
STAmount saReceiverLimit = STAmount (uCurrencyID, uReceiverID);
STAmount saReceiverLimit = STAmount (currency, uReceiverID);
STAmount saBalance = saAmount;
saBalance.setIssuer (ACCOUNT_ONE);
@@ -1528,41 +1577,57 @@ TER LedgerEntrySet::rippleCredit (const uint160& uSenderID, const uint160& uRece
bool bDelete = false;
// YYY Could skip this if rippling in reverse.
if (saBefore > zero // Sender balance was positive.
&& saBalance <= zero // Sender is zero or negative.
&& (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) // Sender reserve is set.
&& !(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple))
&& !sleRippleState->getFieldAmount (!bSenderHigh ? sfLowLimit : sfHighLimit) // Sender trust limit is 0.
&& !sleRippleState->getFieldU32 (!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) // Sender quality in is 0.
&& !sleRippleState->getFieldU32 (!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) // Sender quality out is 0.
if (saBefore > zero
// Sender balance was positive.
&& saBalance <= zero
// Sender is zero or negative.
&& (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
// Sender reserve is set.
&& !(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple))
&& !sleRippleState->getFieldAmount (
!bSenderHigh ? sfLowLimit : sfHighLimit)
// Sender trust limit is 0.
&& !sleRippleState->getFieldU32 (
!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
// Sender quality in is 0.
&& !sleRippleState->getFieldU32 (
!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
// Sender quality out is 0.
{
// Clear the reserve of the sender, possibly delete the line!
SLE::pointer sleSender = entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uSenderID));
auto sleSender = entryCache (
ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uSenderID));
ownerCountAdjust (uSenderID, -1, sleSender);
sleRippleState->setFieldU32 (sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); // Clear reserve flag.
// Clear reserve flag.
sleRippleState->setFieldU32 (
sfFlags,
uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
// Balance is zero, receiver reserve is clear.
bDelete = !saBalance // Balance is zero.
&& !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)); // Receiver reserve is clear.
&& !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
// Receiver reserve is clear.
}
if (bSenderHigh)
saBalance.negate ();
// Want to reflect balance to zero even if we are deleting line.
sleRippleState->setFieldAmount (sfBalance, saBalance); // ONLY: Adjust ripple balance.
sleRippleState->setFieldAmount (sfBalance, saBalance);
// ONLY: Adjust ripple balance.
if (bDelete)
{
terResult = trustDelete (sleRippleState,
bSenderHigh ? uReceiverID : uSenderID,
!bSenderHigh ? uReceiverID : uSenderID);
terResult = trustDelete (
sleRippleState,
bSenderHigh ? uReceiverID : uSenderID,
!bSenderHigh ? uReceiverID : uSenderID);
}
else
{
entryModify (sleRippleState);
terResult = tesSUCCESS;
}
}
@@ -1573,15 +1638,18 @@ TER LedgerEntrySet::rippleCredit (const uint160& uSenderID, const uint160& uRece
// Send regardless of limits.
// --> saAmount: Amount/currency/issuer to deliver to reciever.
// <-- saActual: Amount actually cost. Sender pay's fees.
TER LedgerEntrySet::rippleSend (const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, STAmount& saActual)
TER LedgerEntrySet::rippleSend (
const uint160& uSenderID, const uint160& uReceiverID,
const STAmount& saAmount, STAmount& saActual)
{
const uint160 uIssuerID = saAmount.getIssuer ();
const uint160 issuer = saAmount.getIssuer ();
TER terResult;
assert (!!uSenderID && !!uReceiverID);
assert (uSenderID != uReceiverID);
if (uSenderID == uIssuerID || uReceiverID == uIssuerID || uIssuerID == ACCOUNT_ONE)
if (uSenderID == issuer || uReceiverID == issuer ||
issuer == ACCOUNT_ONE)
{
// Direct send: redeeming IOUs and/or sending own IOUs.
terResult = rippleCredit (uSenderID, uReceiverID, saAmount, false);
@@ -1594,11 +1662,12 @@ TER LedgerEntrySet::rippleSend (const uint160& uSenderID, const uint160& uReceiv
{
// Sending 3rd party IOUs: transit.
STAmount saTransitFee = rippleTransferFee (uSenderID, uReceiverID, uIssuerID, saAmount);
STAmount saTransitFee = rippleTransferFee (
uSenderID, uReceiverID, issuer, saAmount);
saActual = !saTransitFee ? saAmount : saAmount + saTransitFee;
saActual = !saTransitFee ? saAmount : saAmount + saTransitFee;
saActual.setIssuer (uIssuerID); // XXX Make sure this done in + above.
saActual.setIssuer (issuer); // XXX Make sure this done in + above.
WriteLog (lsDEBUG, LedgerEntrySet) << "rippleSend> " <<
RippleAddress::createHumanAccountID (uSenderID) <<
@@ -1607,16 +1676,18 @@ TER LedgerEntrySet::rippleSend (const uint160& uSenderID, const uint160& uReceiv
" fee=" << saTransitFee.getFullText () <<
" cost=" << saActual.getFullText ();
terResult = rippleCredit (uIssuerID, uReceiverID, saAmount);
terResult = rippleCredit (issuer, uReceiverID, saAmount);
if (tesSUCCESS == terResult)
terResult = rippleCredit (uSenderID, uIssuerID, saActual);
terResult = rippleCredit (uSenderID, issuer, saActual);
}
return terResult;
}
TER LedgerEntrySet::accountSend (const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount)
TER LedgerEntrySet::accountSend (
const uint160& uSenderID, const uint160& uReceiverID,
const STAmount& saAmount)
{
TER terResult = tesSUCCESS;
@@ -1624,15 +1695,17 @@ TER LedgerEntrySet::accountSend (const uint160& uSenderID, const uint160& uRecei
if (!saAmount || (uSenderID == uReceiverID))
return tesSUCCESS;
if (saAmount.isNative ())
{
// XRP send which does not check reserve and can do pure adjustment.
SLE::pointer sleSender = !!uSenderID
? entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uSenderID))
? entryCache (ltACCOUNT_ROOT,
Ledger::getAccountRootIndex (uSenderID))
: SLE::pointer ();
SLE::pointer sleReceiver = !!uReceiverID
? entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uReceiverID))
? entryCache (ltACCOUNT_ROOT,
Ledger::getAccountRootIndex (uReceiverID))
: SLE::pointer ();
auto get_balance = [](SLE::pointer acct)

View File

@@ -181,37 +181,69 @@ public:
uint256 getNextLedgerIndex (uint256 const & uHash);
uint256 getNextLedgerIndex (uint256 const & uHash, uint256 const & uEnd);
void ownerCountAdjust (const uint160 & uOwnerID, int iAmount, SLE::ref sleAccountRoot = SLE::pointer ());
void ownerCountAdjust (uint160 const& uOwnerID, int iAmount, SLE::ref sleAccountRoot = SLE::pointer ());
// Offer functions.
TER offerDelete (uint256 const & uOfferIndex);
TER offerDelete (uint256 const & offerIndex);
TER offerDelete (SLE::pointer sleOffer);
// Balance functions.
std::uint32_t rippleTransferRate (const uint160 & uIssuerID);
std::uint32_t rippleTransferRate (const uint160 & uSenderID, const uint160 & uReceiverID, const uint160 & uIssuerID);
STAmount rippleOwed (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID);
STAmount rippleLimit (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID);
std::uint32_t rippleQualityIn (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID,
SField::ref sfLow = sfLowQualityIn, SField::ref sfHigh = sfHighQualityIn);
std::uint32_t rippleQualityOut (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID)
std::uint32_t rippleTransferRate (uint160 const& issuer);
std::uint32_t rippleTransferRate (
uint160 const& uSenderID, uint160 const& uReceiverID,
uint160 const& issuer);
STAmount rippleOwed (
uint160 const& uToAccountID, uint160 const& uFromAccountID,
uint160 const& currency);
STAmount rippleLimit (
uint160 const& uToAccountID, uint160 const& uFromAccountID,
uint160 const& currency);
std::uint32_t rippleQualityIn (
uint160 const& uToAccountID, uint160 const& uFromAccountID,
uint160 const& currency,
SField::ref sfLow = sfLowQualityIn,
SField::ref sfHigh = sfHighQualityIn);
std::uint32_t rippleQualityOut (
uint160 const& uToAccountID, uint160 const& uFromAccountID,
uint160 const& currency)
{
return rippleQualityIn (uToAccountID, uFromAccountID, uCurrencyID, sfLowQualityOut, sfHighQualityOut);
return rippleQualityIn (
uToAccountID, uFromAccountID, currency,
sfLowQualityOut, sfHighQualityOut);
}
STAmount rippleHolds (const uint160 & uAccountID, const uint160 & uCurrencyID, const uint160 & uIssuerID);
STAmount rippleTransferFee (const uint160 & uSenderID, const uint160 & uReceiverID, const uint160 & uIssuerID, const STAmount & saAmount);
TER rippleCredit (const uint160 & uSenderID, const uint160 & uReceiverID, const STAmount & saAmount, bool bCheckIssuer = true);
TER rippleSend (const uint160 & uSenderID, const uint160 & uReceiverID, const STAmount & saAmount, STAmount & saActual);
STAmount rippleHolds (
uint160 const& account, uint160 const& currency,
uint160 const& issuer);
STAmount accountHolds (const uint160 & uAccountID, const uint160 & uCurrencyID, const uint160 & uIssuerID);
STAmount accountFunds (const uint160 & uAccountID, const STAmount & saDefault);
TER accountSend (const uint160 & uSenderID, const uint160 & uReceiverID, const STAmount & saAmount);
STAmount rippleTransferFee (
uint160 const& uSenderID, uint160 const& uReceiverID,
uint160 const& issuer, const STAmount & saAmount);
TER trustCreate (
TER rippleCredit (
uint160 const& uSenderID, uint160 const& uReceiverID,
const STAmount & saAmount, bool bCheckIssuer = true);
TER rippleSend (
uint160 const& uSenderID, uint160 const& uReceiverID,
const STAmount & saAmount, STAmount & saActual);
STAmount accountHolds (
uint160 const& account, uint160 const& currency,
uint160 const& issuer);
STAmount accountFunds (
uint160 const& account, const STAmount & saDefault);
TER accountSend (
uint160 const& uSenderID, uint160 const& uReceiverID,
const STAmount & saAmount);
TER trustCreate (
const bool bSrcHigh,
const uint160 & uSrcAccountID,
const uint160 & uDstAccountID,
uint160 const& uSrcAccountID,
uint160 const& uDstAccountID,
uint256 const & uIndex,
SLE::ref sleAccount,
const bool bAuth,
@@ -220,14 +252,18 @@ public:
const STAmount & saSrcLimit,
const std::uint32_t uSrcQualityIn = 0,
const std::uint32_t uSrcQualityOut = 0);
TER trustDelete (SLE::ref sleRippleState, const uint160 & uLowAccountID, const uint160 & uHighAccountID);
TER trustDelete (
SLE::ref sleRippleState, uint160 const& uLowAccountID,
uint160 const& uHighAccountID);
Json::Value getJson (int) const;
void calcRawMeta (Serializer&, TER result, std::uint32_t index);
// iterator functions
typedef std::map<uint256, LedgerEntrySetEntry>::iterator iterator;
typedef std::map<uint256, LedgerEntrySetEntry>::const_iterator const_iterator;
typedef std::map<uint256, LedgerEntrySetEntry>::iterator iterator;
typedef std::map<uint256, LedgerEntrySetEntry>::const_iterator
const_iterator;
bool isEmpty () const
{
return mEntries.empty ();
@@ -257,27 +293,34 @@ public:
private:
Ledger::pointer mLedger;
std::map<uint256, LedgerEntrySetEntry> mEntries; // cannot be unordered!
typedef ripple::unordered_map<uint256, SLE::pointer> NodeToLedgerEntry;
TransactionMetaSet mSet;
TransactionEngineParams mParams;
int mSeq;
bool mImmutable;
LedgerEntrySet (Ledger::ref ledger, const std::map<uint256, LedgerEntrySetEntry>& e,
const TransactionMetaSet & s, int m) :
mLedger (ledger), mEntries (e), mSet (s), mParams (tapNONE), mSeq (m), mImmutable (false)
{
;
}
LedgerEntrySet (
Ledger::ref ledger, const std::map<uint256, LedgerEntrySetEntry>& e,
const TransactionMetaSet & s, int m) :
mLedger (ledger), mEntries (e), mSet (s), mParams (tapNONE), mSeq (m),
mImmutable (false)
{}
SLE::pointer getForMod (uint256 const & node, Ledger::ref ledger,
ripple::unordered_map<uint256, SLE::pointer>& newMods);
SLE::pointer getForMod (
uint256 const & node, Ledger::ref ledger,
NodeToLedgerEntry& newMods);
bool threadTx (const RippleAddress & threadTo, Ledger::ref ledger,
ripple::unordered_map<uint256, SLE::pointer>& newMods);
bool threadTx (
const RippleAddress & threadTo, Ledger::ref ledger,
NodeToLedgerEntry& newMods);
bool threadTx (SLE::ref threadTo, Ledger::ref ledger, ripple::unordered_map<uint256, SLE::pointer>& newMods);
bool threadTx (
SLE::ref threadTo, Ledger::ref ledger, NodeToLedgerEntry& newMods);
bool threadOwners (SLE::ref node, Ledger::ref ledger, ripple::unordered_map<uint256, SLE::pointer>& newMods);
bool threadOwners (
SLE::ref node, Ledger::ref ledger, NodeToLedgerEntry& newMods);
};
inline LedgerEntrySet::iterator range_begin (LedgerEntrySet& x)

View File

@@ -2882,7 +2882,7 @@ void NetworkOPsImp::getBookPage (Ledger::pointer lpLedger, const uint160& uTaker
bool bDirectAdvance = true;
SLE::pointer sleOfferDir;
uint256 uOfferIndex;
uint256 offerIndex;
unsigned int uBookEntry;
STAmount saDirRate;
@@ -2913,16 +2913,16 @@ void NetworkOPsImp::getBookPage (Ledger::pointer lpLedger, const uint160& uTaker
uTipIndex = sleOfferDir->getIndex ();
saDirRate = STAmount::setRate (Ledger::getQuality (uTipIndex));
lesActive.dirFirst (uTipIndex, sleOfferDir, uBookEntry, uOfferIndex);
lesActive.dirFirst (uTipIndex, sleOfferDir, uBookEntry, offerIndex);
m_journal.trace << "getBookPage: uTipIndex=" << uTipIndex;
m_journal.trace << "getBookPage: uOfferIndex=" << uOfferIndex;
m_journal.trace << "getBookPage: offerIndex=" << offerIndex;
}
}
if (!bDone)
{
SLE::pointer sleOffer = lesActive.entryCache (ltOFFER, uOfferIndex);
SLE::pointer sleOffer = lesActive.entryCache (ltOFFER, offerIndex);
if (sleOffer)
{
@@ -3030,13 +3030,13 @@ void NetworkOPsImp::getBookPage (Ledger::pointer lpLedger, const uint160& uTaker
m_journal.warning << "Missing offer";
}
if (!lesActive.dirNext (uTipIndex, sleOfferDir, uBookEntry, uOfferIndex))
if (!lesActive.dirNext (uTipIndex, sleOfferDir, uBookEntry, offerIndex))
{
bDirectAdvance = true;
}
else
{
m_journal.trace << "getBookPage: uOfferIndex=" << uOfferIndex;
m_journal.trace << "getBookPage: offerIndex=" << offerIndex;
}
}
}

View File

@@ -27,13 +27,7 @@ namespace path {
// OPTIMIZE: When calculating path increment, note if increment consumes all
// liquidity. No need to revisit path in the future if all liquidity is used.
//
// VFALCO TODO Update the comment for this function, the argument list no
// resembles the comment
//
// Provide a better explanation for what this function does.
// nodeAdvance advances through offers in an order book.
// If needed, advance to next funded offer.
// - Automatically advances to first offer.
// --> bEntryAdvance: true, to advance to next entry. false, recalculate.
@@ -47,29 +41,13 @@ TER nodeAdvance (
{
auto& previousNode = pathState.nodes()[nodeIndex - 1];
auto& node = pathState.nodes()[nodeIndex];
uint256& uDirectTip = node.uDirectTip;
uint256& uDirectEnd = node.uDirectEnd;
bool& bDirectAdvance = node.bDirectAdvance;
bool& bDirectRestart = node.bDirectRestart;
SLE::pointer& sleDirectDir = node.sleDirectDir;
STAmount& saOfrRate = node.saOfrRate;
bool& bEntryAdvance = node.bEntryAdvance;
unsigned int& uEntry = node.uEntry;
uint256& uOfferIndex = node.uOfferIndex;
SLE::pointer& sleOffer = node.sleOffer;
uint160& uOfrOwnerID = node.uOfrOwnerID;
STAmount& saOfferFunds = node.saOfferFunds;
STAmount& saTakerPays = node.saTakerPays;
STAmount& saTakerGets = node.saTakerGets;
bool& bFundsDirty = node.bFundsDirty;
TER resultCode = tesSUCCESS;
// Taker is the active party against an offer in the ledger - the entity
// that is taking advantage of an offer in the order book.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: TakerPays:"
<< saTakerPays << " TakerGets:" << saTakerGets;
<< node.saTakerPays << " TakerGets:" << node.saTakerGets;
int loopCount = 0;
@@ -87,63 +65,70 @@ TER nodeAdvance (
return tefEXCEPTION;
}
bool bDirectDirDirty = false;
bool bDirectDirDirty = false;
if (!uDirectTip)
if (!node.uDirectTip)
{
// Need to initialize current node.
uDirectTip = Ledger::getBookBase (
previousNode.uCurrencyID, previousNode.uIssuerID,
node.uCurrencyID,
node.uIssuerID);
uDirectEnd = Ledger::getQualityNext (uDirectTip);
node.uDirectTip = Ledger::getBookBase (
previousNode.currency_, previousNode.issuer_,
node.currency_,
node.issuer_);
node.uDirectEnd = Ledger::getQualityNext (node.uDirectTip);
sleDirectDir = rippleCalc.mActiveLedger.entryCache (ltDIR_NODE, uDirectTip);
// TODO(tom): it seems impossible that any actual offers with
// quality == 0 could occur - we should disallow them, and clear
// sleDirectDir without the database call in the next line.
node.sleDirectDir = rippleCalc.mActiveLedger.entryCache (
ltDIR_NODE, node.uDirectTip);
// Associated vars are dirty, if found it.
bDirectDirDirty = !!sleDirectDir;
bDirectDirDirty = !!node.sleDirectDir;
// Advance, if didn't find it. Normal not to be unable to lookup
// firstdirectory. Maybe even skip this lookup.
bDirectAdvance = !sleDirectDir;
bDirectRestart = false;
node.bDirectAdvance = !node.sleDirectDir;
node.bDirectRestart = false;
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: Initialize node:"
<< " uDirectTip=" << uDirectTip
<<" uDirectEnd=" << uDirectEnd
<< " bDirectAdvance=" << bDirectAdvance;
<< " node.uDirectTip=" << node.uDirectTip
<<" node.uDirectEnd=" << node.uDirectEnd
<< " node.bDirectAdvance=" << node.bDirectAdvance;
}
if (bDirectAdvance || bDirectRestart)
if (node.bDirectAdvance || node.bDirectRestart)
{
// Get next quality.
if (bDirectAdvance)
if (node.bDirectAdvance)
{
uDirectTip = rippleCalc.mActiveLedger.getNextLedgerIndex (
uDirectTip, uDirectEnd);
// This works because the Merkel radix tree is ordered by key so
// we can go to the next one in O(1).
node.uDirectTip = rippleCalc.mActiveLedger.getNextLedgerIndex (
node.uDirectTip, node.uDirectEnd);
}
bDirectDirDirty = true;
bDirectAdvance = false;
bDirectRestart = false;
node.bDirectAdvance = false;
node.bDirectRestart = false;
if (!!uDirectTip)
if (node.uDirectTip != zero)
{
// Have another quality directory.
// We didn't run off the end of this order book and found
// another quality directory.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: Quality advance: uDirectTip="
<< uDirectTip;
<< "nodeAdvance: Quality advance: node.uDirectTip="
<< node.uDirectTip;
sleDirectDir = rippleCalc.mActiveLedger.entryCache (ltDIR_NODE, uDirectTip);
node.sleDirectDir = rippleCalc.mActiveLedger.entryCache (ltDIR_NODE, node.uDirectTip);
}
else if (bReverse)
{
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: No more offers.";
uOfferIndex = 0;
node.offerIndex_ = 0;
break;
}
else
@@ -161,33 +146,35 @@ TER nodeAdvance (
if (bDirectDirDirty)
{
saOfrRate = STAmount::setRate (Ledger::getQuality (uDirectTip));
// Our quality changed since last iteration.
// Use the rate from the directory.
node.saOfrRate = STAmount::setRate (Ledger::getQuality (node.uDirectTip));
// For correct ratio
uEntry = 0;
bEntryAdvance = true;
node.uEntry = 0;
node.bEntryAdvance = true;
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: directory dirty: saOfrRate="
<< saOfrRate;
<< "nodeAdvance: directory dirty: node.saOfrRate="
<< node.saOfrRate;
}
if (!bEntryAdvance)
if (!node.bEntryAdvance)
{
if (bFundsDirty)
if (node.bFundsDirty)
{
// We were called again probably merely to update structure
// variables.
saTakerPays = sleOffer->getFieldAmount (sfTakerPays);
saTakerGets = sleOffer->getFieldAmount (sfTakerGets);
node.saTakerPays = node.sleOffer->getFieldAmount (sfTakerPays);
node.saTakerGets = node.sleOffer->getFieldAmount (sfTakerGets);
saOfferFunds = rippleCalc.mActiveLedger.accountFunds (
uOfrOwnerID, saTakerGets);
// Funds left.
bFundsDirty = false;
node.saOfferFunds = rippleCalc.mActiveLedger.accountFunds (
node.offerOwnerAccount_, node.saTakerGets);
node.bFundsDirty = false;
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: funds dirty: saOfrRate="
<< saOfrRate;
<< "nodeAdvance: funds dirty: node.saOfrRate="
<< node.saOfrRate;
}
else
{
@@ -195,7 +182,8 @@ TER nodeAdvance (
}
}
else if (!rippleCalc.mActiveLedger.dirNext (
uDirectTip, sleDirectDir, uEntry, uOfferIndex))
node.uDirectTip, node.sleDirectDir, node.uEntry, node.offerIndex_))
// This is the only place that offerIndex_ changes.
{
// Failed to find an entry in directory.
// Do another cur directory iff bMultiQuality
@@ -205,61 +193,69 @@ TER nodeAdvance (
// only path.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: next quality";
bDirectAdvance = true; // Process next quality.
node.bDirectAdvance = true; // Process next quality.
}
else if (!bReverse)
{
// We didn't run dry going backwards - why are we running dry
// going forwards - this should be impossible!
// TODO(tom): these warnings occur in production! They
// shouldn't.
WriteLog (lsWARNING, RippleCalc)
<< "nodeAdvance: unreachable: ran out of offers";
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING :
tecFAILED_PROCESSING;
// TEMPORARY
}
else
{
// Ran off end of offers.
bEntryAdvance = false; // Done.
uOfferIndex = 0; // Report nore more entries.
node.bEntryAdvance = false; // Done.
node.offerIndex_ = 0; // Report no more entries.
}
}
else
{
// Got a new offer.
sleOffer = rippleCalc.mActiveLedger.entryCache (ltOFFER, uOfferIndex);
node.sleOffer = rippleCalc.mActiveLedger.entryCache (
ltOFFER, node.offerIndex_);
if (!sleOffer)
if (!node.sleOffer)
{
// Corrupt directory that points to an entry that doesn't exist.
// This has happened in production.
WriteLog (lsWARNING, RippleCalc) <<
"Missing offer in directory";
bEntryAdvance = true;
node.bEntryAdvance = true;
}
else
{
uOfrOwnerID = sleOffer->getFieldAccount160 (sfAccount);
saTakerPays = sleOffer->getFieldAmount (sfTakerPays);
saTakerGets = sleOffer->getFieldAmount (sfTakerGets);
node.offerOwnerAccount_
= node.sleOffer->getFieldAccount160 (sfAccount);
node.saTakerPays = node.sleOffer->getFieldAmount (sfTakerPays);
node.saTakerGets = node.sleOffer->getFieldAmount (sfTakerGets);
const AccountCurrencyIssuer asLine (
uOfrOwnerID, node.uCurrencyID, node.uIssuerID);
node.offerOwnerAccount_, node.currency_, node.issuer_);
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: uOfrOwnerID="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=" << saTakerGets
<< " uOfferIndex=" << uOfferIndex;
<< "nodeAdvance: offerOwnerAccount_="
<< RippleAddress::createHumanAccountID (node.offerOwnerAccount_)
<< " node.saTakerPays=" << node.saTakerPays
<< " node.saTakerGets=" << node.saTakerGets
<< " node.offerIndex_=" << node.offerIndex_;
if (sleOffer->isFieldPresent (sfExpiration) &&
(sleOffer->getFieldU32 (sfExpiration) <=
if (node.sleOffer->isFieldPresent (sfExpiration) &&
(node.sleOffer->getFieldU32 (sfExpiration) <=
rippleCalc.mActiveLedger.getLedger ()->getParentCloseTimeNC ()))
{
// Offer is expired.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: expired offer";
rippleCalc.mUnfundedOffers.insert(uOfferIndex);
rippleCalc.mUnfundedOffers.insert(node.offerIndex_);
continue;
}
else if (saTakerPays <= zero || saTakerGets <= zero)
if (node.saTakerPays <= zero || node.saTakerGets <= zero)
{
// Offer has bad amounts. Offers should never have a bad
// amounts.
@@ -267,26 +263,27 @@ TER nodeAdvance (
if (bReverse)
{
// Past internal error, offer had bad amounts.
// This has occurred in production.
WriteLog (lsWARNING, RippleCalc)
<< "nodeAdvance: PAST INTERNAL ERROR:"
<< " OFFER NON-POSITIVE:"
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=%s" << saTakerGets;
<< "nodeAdvance: PAST INTERNAL ERROR"
<< " REVERSE: OFFER NON-POSITIVE:"
<< " node.saTakerPays=" << node.saTakerPays
<< " node.saTakerGets=%s" << node.saTakerGets;
// Mark offer for always deletion.
rippleCalc.mUnfundedOffers.insert (uOfferIndex);
rippleCalc.mUnfundedOffers.insert (node.offerIndex_);
}
else if (rippleCalc.mUnfundedOffers.find (uOfferIndex) !=
rippleCalc.mUnfundedOffers.end ())
else if (rippleCalc.mUnfundedOffers.find (node.offerIndex_)
!= rippleCalc.mUnfundedOffers.end ())
{
// Past internal error, offer was found failed to place
// this in mUnfundedOffers.
// Just skip it. It will be deleted.
WriteLog (lsDEBUG, RippleCalc)
<< "nodeAdvance: PAST INTERNAL ERROR:"
<< " OFFER NON-POSITIVE:"
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=" << saTakerGets;
<< "nodeAdvance: PAST INTERNAL ERROR "
<< " FORWARD CONFIRM: OFFER NON-POSITIVE:"
<< " node.saTakerPays=" << node.saTakerPays
<< " node.saTakerGets=" << node.saTakerGets;
}
else
@@ -294,10 +291,11 @@ TER nodeAdvance (
// Reverse should have previously put bad offer in list.
// An internal error previously left a bad offer.
WriteLog (lsWARNING, RippleCalc)
<< "nodeAdvance: INTERNAL ERROR:"
<<" OFFER NON-POSITIVE:"
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=" << saTakerGets;
<< "nodeAdvance: INTERNAL ERROR"
<<" FORWARD NEWLY FOUND: OFFER NON-POSITIVE:"
<< " node.saTakerPays=" << node.saTakerPays
<< " node.saTakerGets=" << node.saTakerGets;
// Don't process at all, things are in an unexpected
// state for this transactions.
@@ -324,7 +322,7 @@ TER nodeAdvance (
// forward.
if (bFoundForward &&
itForward->second != nodeIndex &&
uOfrOwnerID != node.uIssuerID)
node.offerOwnerAccount_ != node.issuer_)
{
// Temporarily unfunded. Another node uses this source,
// ignore in this offer.
@@ -344,7 +342,7 @@ TER nodeAdvance (
// applying offers in reverse.
if (bFoundReverse &&
itReverse->second != nodeIndex &&
uOfrOwnerID != node.uIssuerID)
node.offerOwnerAccount_ != node.issuer_)
{
// Temporarily unfunded. Another node uses this source,
// ignore in this offer.
@@ -361,10 +359,10 @@ TER nodeAdvance (
// Only the current node is allowed to use the source.
saOfferFunds = rippleCalc.mActiveLedger.accountFunds
(uOfrOwnerID, saTakerGets); // Funds held.
node.saOfferFunds = rippleCalc.mActiveLedger.accountFunds
(node.offerOwnerAccount_, node.saTakerGets); // Funds held.
if (saOfferFunds <= zero)
if (node.saOfferFunds <= zero)
{
// Offer is unfunded.
WriteLog (lsTRACE, RippleCalc)
@@ -376,7 +374,7 @@ TER nodeAdvance (
// That is, even if this offer fails due to fill or kill
// still do deletions.
// Mark offer for always deletion.
rippleCalc.mUnfundedOffers.insert (uOfferIndex);
rippleCalc.mUnfundedOffers.insert (node.offerIndex_);
}
else
{
@@ -395,26 +393,26 @@ TER nodeAdvance (
// Consider source mentioned by current path state.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: remember="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< RippleAddress::createHumanAccountID (node.offerOwnerAccount_)
<< "/"
<< STAmount::createHumanCurrency (node.uCurrencyID)
<< STAmount::createHumanCurrency (node.currency_)
<< "/"
<< RippleAddress::createHumanAccountID (node.uIssuerID);
<< RippleAddress::createHumanAccountID (node.issuer_);
pathState.reverse().insert (std::make_pair (asLine, nodeIndex));
}
bFundsDirty = false;
bEntryAdvance = false;
node.bFundsDirty = false;
node.bEntryAdvance = false;
}
}
}
while (resultCode == tesSUCCESS && (bEntryAdvance || bDirectAdvance));
while (resultCode == tesSUCCESS && (node.bEntryAdvance || node.bDirectAdvance));
if (resultCode == tesSUCCESS)
{
WriteLog (lsTRACE, RippleCalc)
<< "nodeAdvance: uOfferIndex=" << uOfferIndex;
<< "nodeAdvance: node.offerIndex_=" << node.offerIndex_;
}
else
{

View File

@@ -27,7 +27,8 @@ namespace path {
// For current offer, get input from deliver/limbo and output to next account or
// deliver for next offers.
//
// <-- node.saFwdDeliver: For nodeAccountFwd to know how much went through
// <-- node.saFwdDeliver: For computeForwardLiquidityForAccount to know
// how much went through
// --> node.saRevDeliver: Do not exceed.
TER nodeDeliverFwd (
@@ -46,33 +47,19 @@ TER nodeDeliverFwd (
auto& node = pathState.nodes()[nodeIndex];
auto& nextNode = pathState.nodes()[nodeIndex + 1];
const uint160& nextAccountID = nextNode.uAccountID;
const uint160& uCurCurrencyID = node.uCurrencyID;
const uint160& uCurIssuerID = node.uIssuerID;
uint256 const& uOfferIndex = node.uOfferIndex;
const uint160& uPrvCurrencyID = previousNode.uCurrencyID;
const uint160& uPrvIssuerID = previousNode.uIssuerID;
const STAmount& saInTransRate = previousNode.saTransferRate;
const STAmount& saCurDeliverMax = node.saRevDeliver;
// Don't deliver more than wanted.
STAmount& saCurDeliverAct = node.saFwdDeliver;
// Zeroed in reverse pass.
uint256& uDirectTip = node.uDirectTip;
bool& bDirectRestart = node.bDirectRestart;
if (bMultiQuality)
uDirectTip = 0; // Restart book searching.
node.uDirectTip = 0; // Restart book searching.
else
bDirectRestart = true; // Restart at same quality.
node.bDirectRestart = true; // Restart at same quality.
saInAct.clear (saInReq);
saInFees.clear (saInReq);
int loopCount = 0;
// XXX Perhaps make sure do not exceed saCurDeliverMax as another way to
// XXX Perhaps make sure do not exceed node.saRevDeliver as another way to
// stop?
while (resultCode == tesSUCCESS && saInAct + saInFees < saInReq)
{
@@ -81,11 +68,12 @@ TER nodeDeliverFwd (
{
WriteLog (lsWARNING, RippleCalc)
<< "nodeDeliverFwd: max loops cndf";
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING :
tecFAILED_PROCESSING;
}
// Determine values for pass to adjust saInAct, saInFees, and
// saCurDeliverAct.
// node.saFwdDeliver.
resultCode = nodeAdvance (
rippleCalc,
nodeIndex, pathState, bMultiQuality || saInAct == zero, false);
@@ -94,31 +82,30 @@ TER nodeDeliverFwd (
if (resultCode != tesSUCCESS)
{
}
else if (!uOfferIndex)
else if (!node.offerIndex_)
{
WriteLog (lsWARNING, RippleCalc)
<< "nodeDeliverFwd: INTERNAL ERROR: Ran out of offers.";
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING
: tecFAILED_PROCESSING;
}
else if (resultCode == tesSUCCESS)
{
// Doesn't charge input. Input funds are in limbo.
bool& bEntryAdvance = node.bEntryAdvance;
STAmount& saOfrRate = node.saOfrRate;
uint256& uOfferIndex = node.uOfferIndex;
SLE::pointer& sleOffer = node.sleOffer;
const uint160& uOfrOwnerID = node.uOfrOwnerID;
bool& bFundsDirty = node.bFundsDirty;
STAmount& saOfferFunds = node.saOfferFunds;
STAmount& saTakerPays = node.saTakerPays;
STAmount& saTakerGets = node.saTakerGets;
const STAmount saInFeeRate
= !uPrvCurrencyID // XRP.
|| uInAccountID == uPrvIssuerID // Sender is issuer.
|| uOfrOwnerID == uPrvIssuerID // Reciever is issuer.
? saOne // No fee.
: saInTransRate; // Transfer rate of issuer.
// There's no fee if we're transferring XRP, if the sender is the
// issuer, or if the receiver is the issuer.
bool noFee = isXRP(previousNode.currency_)
|| uInAccountID == previousNode.issuer_
|| node.offerOwnerAccount_ == previousNode.issuer_;
const STAmount saInFeeRate = noFee ? saOne
: previousNode.transferRate_; // Transfer rate of issuer.
// First calculate assuming no output fees: saInPassAct,
// saInPassFees, saOutPassAct.
@@ -128,7 +115,7 @@ TER nodeDeliverFwd (
// Offer maximum out - limit by most to deliver.
STAmount saOutPassFunded = std::min (
saOutFunded, saCurDeliverMax - saCurDeliverAct);
saOutFunded, node.saRevDeliver - node.saFwdDeliver);
// Offer maximum in - Limited by by payout.
STAmount saInFunded = STAmount::mulRound (
@@ -185,8 +172,8 @@ TER nodeDeliverFwd (
<< "nodeDeliverFwd: Microscopic offer unfunded.";
// After math offer is effectively unfunded.
pathState.becameUnfunded().push_back (uOfferIndex);
bEntryAdvance = true;
pathState.becameUnfunded().push_back (node.offerIndex_);
node.bEntryAdvance = true;
continue;
}
else if (!saInFunded)
@@ -196,11 +183,11 @@ TER nodeDeliverFwd (
<< "nodeDeliverFwd: UNREACHABLE REACHED";
// After math offer is effectively unfunded.
pathState.becameUnfunded().push_back (uOfferIndex);
bEntryAdvance = true;
pathState.becameUnfunded().push_back (node.offerIndex_);
node.bEntryAdvance = true;
continue;
}
else if (!!nextAccountID)
else if (!!nextNode.account_)
{
// ? --> OFFER --> account
// Input fees: vary based upon the consumed offer's owner.
@@ -212,17 +199,17 @@ TER nodeDeliverFwd (
WriteLog (lsTRACE, RippleCalc)
<< "nodeDeliverFwd: ? --> OFFER --> account:"
<< " uOfrOwnerID="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< " nextAccountID="
<< RippleAddress::createHumanAccountID (nextAccountID)
<< " offerOwnerAccount_="
<< RippleAddress::createHumanAccountID (node.offerOwnerAccount_)
<< " nextNode.account_="
<< RippleAddress::createHumanAccountID (nextNode.account_)
<< " saOutPassAct=" << saOutPassAct
<< " saOutFunded=%s" << saOutFunded;
// Output: Debit offer owner, send XRP or non-XPR to next
// account.
resultCode = rippleCalc.mActiveLedger.accountSend (
uOfrOwnerID, nextAccountID, saOutPassAct);
node.offerOwnerAccount_, nextNode.account_, saOutPassAct);
if (resultCode != tesSUCCESS)
break;
@@ -245,7 +232,7 @@ TER nodeDeliverFwd (
nodeIndex + 1,
pathState,
bMultiQuality,
uOfrOwnerID, // --> Current holder.
node.offerOwnerAccount_, // --> Current holder.
saOutPassMax, // --> Amount available.
saOutPassAct, // <-- Amount delivered.
saOutPassFees); // <-- Fees charged.
@@ -277,9 +264,9 @@ TER nodeDeliverFwd (
// Do outbound debiting.
// Send to issuer/limbo total amount including fees (issuer gets
// fees).
auto id = !!uCurCurrencyID ? uCurIssuerID : ACCOUNT_XRP;
auto id = !!node.currency_ ? Account(node.issuer_) : XRP_ACCOUNT;
auto outPassTotal = saOutPassAct + saOutPassFees;
rippleCalc.mActiveLedger.accountSend (uOfrOwnerID, id, outPassTotal);
rippleCalc.mActiveLedger.accountSend (node.offerOwnerAccount_, id, outPassTotal);
WriteLog (lsTRACE, RippleCalc)
<< "nodeDeliverFwd: ? --> OFFER --> offer:"
@@ -305,13 +292,12 @@ TER nodeDeliverFwd (
// Credit offer owner from in issuer/limbo (input transfer fees left
// with owner). Don't attempt to have someone credit themselves, it
// is redundant.
if (!uPrvCurrencyID // Always credit XRP from limbo.
|| uInAccountID != uOfrOwnerID) // Never send non-XRP to the
// same account.
if (!previousNode.currency_
|| uInAccountID != node.offerOwnerAccount_)
{
auto id = !!uPrvCurrencyID ? uInAccountID : ACCOUNT_XRP;
auto id = !!previousNode.currency_ ? uInAccountID : ACCOUNT_XRP;
resultCode = rippleCalc.mActiveLedger.accountSend (
id, uOfrOwnerID, saInPassAct);
id, node.offerOwnerAccount_, saInPassAct);
if (resultCode != tesSUCCESS)
break;
@@ -333,7 +319,8 @@ TER nodeDeliverFwd (
// If mOpenLedger, then ledger is not final, can vote no.
resultCode = rippleCalc.mOpenLedger
? telFAILED_PROCESSING : tecFAILED_PROCESSING;
? telFAILED_PROCESSING
: tecFAILED_PROCESSING;
break;
}
@@ -351,8 +338,8 @@ TER nodeDeliverFwd (
<< " saOutPassAct=" << saOutPassAct
<< " saOutFunded=" << saOutFunded;
pathState.becameUnfunded().push_back (uOfferIndex);
bEntryAdvance = true;
pathState.becameUnfunded().push_back (node.offerIndex_);
node.bEntryAdvance = true;
}
else
{
@@ -368,8 +355,8 @@ TER nodeDeliverFwd (
saInFees += saInPassFees;
// Adjust amount available to next node.
saCurDeliverAct = std::min (saCurDeliverMax,
saCurDeliverAct + saOutPassAct);
node.saFwdDeliver = std::min (node.saRevDeliver,
node.saFwdDeliver + saOutPassAct);
}
}

View File

@@ -25,8 +25,8 @@ namespace ripple {
namespace path {
// At the right most node of a list of consecutive offer nodes, given the amount
// requested to be delivered, push toward node 0 the amount requested for
// previous nodes to know how much to deliver.
// requested to be delivered, push towards the left nodes the amount requested
// for the right nodes so we can compute how much to deliver from the source.
//
// Between offer nodes, the fee charged may vary. Therefore, process one
// inbound offer at a time. Propagate the inbound offer's requirements to the
@@ -51,11 +51,6 @@ TER nodeDeliverRev (
auto& previousNode = pathState.nodes()[nodeIndex - 1];
auto& node = pathState.nodes()[nodeIndex];
const uint160& uCurIssuerID = node.uIssuerID;
const uint160& uPrvAccountID = previousNode.uAccountID;
const STAmount& saTransferRate = node.saTransferRate;
// Transfer rate of the TakerGets issuer.
STAmount& saPrvDlvReq = previousNode.saRevDeliver;
// Accumulation of what the previous node must deliver.
@@ -67,8 +62,8 @@ TER nodeDeliverRev (
else
bDirectRestart = true; // Restart at same quality.
// YYY Note this gets zeroed on each increment, ideally only on first
// increment, then it could be a limit on the forward pass.
// Possible optimization: Note this gets zeroed on each increment, ideally
// only on first increment, then it could be a limit on the forward pass.
saOutAct.clear (saOutReq);
WriteLog (lsTRACE, RippleCalc)
@@ -77,7 +72,7 @@ TER nodeDeliverRev (
<< " saOutReq=" << saOutReq
<< " saPrvDlvReq=" << saPrvDlvReq;
assert (!!saOutReq);
assert (saOutReq != zero);
int loopCount = 0;
@@ -87,14 +82,13 @@ TER nodeDeliverRev (
if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS)
{
WriteLog (lsFATAL, RippleCalc) << "loop count exceeded";
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING :
tecFAILED_PROCESSING;
}
bool& bEntryAdvance = node.bEntryAdvance;
STAmount& saOfrRate = node.saOfrRate;
uint256& uOfferIndex = node.uOfferIndex;
SLE::pointer& sleOffer = node.sleOffer;
const uint160& uOfrOwnerID = node.uOfrOwnerID;
bool& bFundsDirty = node.bFundsDirty;
STAmount& saOfferFunds = node.saOfferFunds;
STAmount& saTakerPays = node.saTakerPays;
@@ -106,27 +100,27 @@ TER nodeDeliverRev (
nodeIndex, pathState, bMultiQuality || saOutAct == zero, true);
// If needed, advance to next funded offer.
if (resultCode != tesSUCCESS || !uOfferIndex)
if (resultCode != tesSUCCESS || !node.offerIndex_)
{
// Error or out of offers.
break;
}
auto const hasFee = uOfrOwnerID == uCurIssuerID
|| uOutAccountID == uCurIssuerID; // Issuer sending or receiving.
auto const hasFee = node.offerOwnerAccount_ == node.issuer_
|| uOutAccountID == node.issuer_; // Issuer sending or receiving.
const STAmount saOutFeeRate = hasFee
? saOne // No fee.
: saTransferRate; // Transfer rate of issuer.
: node.transferRate_; // Transfer rate of issuer.
WriteLog (lsTRACE, RippleCalc)
<< "nodeDeliverRev:"
<< " uOfrOwnerID="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< " offerOwnerAccount_="
<< RippleAddress::createHumanAccountID (node.offerOwnerAccount_)
<< " uOutAccountID="
<< RippleAddress::createHumanAccountID (uOutAccountID)
<< " uCurIssuerID="
<< RippleAddress::createHumanAccountID (uCurIssuerID)
<< " saTransferRate=" << saTransferRate
<< " node.issuer_="
<< RippleAddress::createHumanAccountID (node.issuer_)
<< " node.transferRate_=" << node.transferRate_
<< " saOutFeeRate=" << saOutFeeRate;
if (bMultiQuality)
@@ -151,7 +145,7 @@ TER nodeDeliverRev (
<< " saRateMax=" << saRateMax
<< " saOutFeeRate=" << saOutFeeRate;
break; // Done. Don't bother looking for smaller saTransferRates.
break; // Done. Don't bother looking for smaller transferRates.
}
else if (saOutFeeRate < saRateMax)
{
@@ -241,7 +235,7 @@ TER nodeDeliverRev (
continue;
}
// Find out input amount actually available at current rate.
else if (!!uPrvAccountID)
else if (!!previousNode.account_)
{
// account --> OFFER --> ?
// Due to node expansion, previous is guaranteed to be the issuer.
@@ -271,7 +265,7 @@ TER nodeDeliverRev (
nodeIndex - 1,
pathState,
bMultiQuality,
uOfrOwnerID,
node.offerOwnerAccount_,
saInPassReq,
saInPassAct);
@@ -315,14 +309,14 @@ TER nodeDeliverRev (
//
// Must reset balances when going forward to perform actual transfers.
resultCode = rippleCalc.mActiveLedger.accountSend (
uOfrOwnerID, uCurIssuerID, saOutPassAct);
node.offerOwnerAccount_, node.issuer_, saOutPassAct);
if (resultCode != tesSUCCESS)
break;
// Adjust offer
STAmount saTakerGetsNew = saTakerGets - saOutPassAct;
STAmount saTakerPaysNew = saTakerPays - saInPassAct;
STAmount saTakerGetsNew = saTakerGets - saOutPassAct;
STAmount saTakerPaysNew = saTakerPays - saInPassAct;
if (saTakerPaysNew < zero || saTakerGetsNew < zero)
{
@@ -332,7 +326,8 @@ TER nodeDeliverRev (
<< " saTakerGetsNew=%s" << saTakerGetsNew;
// If mOpenLedger then ledger is not final, can vote no.
resultCode = rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
resultCode = rippleCalc.mOpenLedger ? telFAILED_PROCESSING
: tecFAILED_PROCESSING;
break;
}
@@ -347,14 +342,14 @@ TER nodeDeliverRev (
WriteLog (lsDEBUG, RippleCalc)
<< "nodeDeliverRev: offer became unfunded.";
bEntryAdvance = true; // XXX When don't we want to set advance?
bEntryAdvance = true; // XXX When don't we want to set advance?
}
else
{
assert (saOutPassAct < saTakerGets);
}
saOutAct += saOutPassAct;
saOutAct += saOutPassAct;
// Accumulate what is to be delivered from previous node.
saPrvDlvReq += saInPassAct;
}
@@ -366,10 +361,10 @@ TER nodeDeliverRev (
assert (saOutAct <= saOutReq);
// XXX Perhaps need to check if partial is okay to relax this?
if (resultCode == tesSUCCESS && !saOutAct)
resultCode = tecPATH_DRY;
// Unable to meet request, consider path dry.
// Design invariant: if nothing was actually delivered, return tecPATH_DRY.
WriteLog (lsTRACE, RippleCalc)
<< "nodeDeliverRev<"

View File

@@ -1,120 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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/module/app/paths/Calculators.h>
#include <ripple/module/app/paths/RippleCalc.h>
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// Called to drive the from the first offer node in a chain.
//
// - Offer input is in issuer/limbo.
// - Current offers consumed.
// - Current offer owners debited.
// - Transfer fees credited to issuer.
// - Payout to issuer or limbo.
// - Deliver is set without transfer fees.
TER nodeOfferFwd (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
const bool bMultiQuality
)
{
TER resultCode;
auto& previousNode = pathState.nodes() [nodeIndex - 1];
if (!!previousNode.uAccountID)
{
// Previous is an account node, resolve its deliver.
STAmount saInAct;
STAmount saInFees;
resultCode = nodeDeliverFwd (
rippleCalc,
nodeIndex,
pathState,
bMultiQuality,
previousNode.uAccountID,
previousNode.saFwdDeliver, // Previous is sending this much.
saInAct,
saInFees);
assert (resultCode != tesSUCCESS ||
previousNode.saFwdDeliver == saInAct + saInFees);
}
else
{
// Previous is an offer. Deliver has already been resolved.
resultCode = tesSUCCESS;
}
return resultCode;
}
// Called to drive from the last offer node in a chain.
TER nodeOfferRev (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
const bool bMultiQuality)
{
TER resultCode;
auto& node = pathState.nodes() [nodeIndex];
auto& nextNode = pathState.nodes() [nodeIndex + 1];
if (!!nextNode.uAccountID)
{
// Next is an account node, resolve current offer node's deliver.
STAmount saDeliverAct;
WriteLog (lsTRACE, RippleCalc)
<< "nodeOfferRev: OFFER --> account:"
<< " nodeIndex=" << nodeIndex
<< " saRevDeliver=" << node.saRevDeliver;
resultCode = nodeDeliverRev (
rippleCalc,
nodeIndex,
pathState,
bMultiQuality,
nextNode.uAccountID,
// The next node wants the current node to deliver this much:
node.saRevDeliver,
saDeliverAct);
}
else
{
WriteLog (lsTRACE, RippleCalc)
<< "nodeOfferRev: OFFER --> offer: nodeIndex=" << nodeIndex;
// Next is an offer. Deliver has already been resolved.
resultCode = tesSUCCESS;
}
return resultCode;
}
} // path
} // ripple

View File

@@ -32,7 +32,7 @@ namespace ripple {
/** RippleCalc calculates the quality of a payment path.
Quality is the amount of input required to produce a given output along a
specified path. Most people would call this an exchange rate.
specified path - another name for this is exchange rate.
*/
class RippleCalc
{
@@ -65,32 +65,57 @@ void pathNext (
PathState& pathState, const bool bMultiQuality,
const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
TER nodeRev (
// The next section contains functions that compute the liqudity along a path,
// either backward or forward.
//
// We need to do these computations twice - once backward to figure out the
// maximum possible liqiudity along a path, and then forward to compute the
// actual liquidity of the paths we actually chose.
//
// These functions were originally methods on RippleCalc, but will end up as
// methods on another class which keeps the state that's currently passed around
// as many parameters. For the moment, they're free functions that take a
// RippleCalc (the path calculation "God object").
//
// Many of these routines use recursion to loop over all nodes in a path.
// TODO(tom): replace this recursion with a loop.
TER computeReverseLiqudity (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER nodeFwd (
TER computeForwardLiqudity (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER nodeOfferRev (
TER computeReverseLiquidityForOffer (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER nodeOfferFwd (
TER computeForwardLiquidityForOffer (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER nodeAccountRev (
TER computeReverseLiquidityForAccount (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER nodeAccountFwd (
TER computeForwardLiquidityForAccount (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
void computeRippleLiquidity (
RippleCalc&,
const std::uint32_t uQualityIn,
const std::uint32_t uQualityOut,
const STAmount& saPrvReq,
const STAmount& saCurReq,
STAmount& saPrvAct,
STAmount& saCurAct,
std::uint64_t& uRateMax);
// To send money out of an account.
TER nodeAdvance (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality,
bool bReverse);
// To deliver from an order book.
// To deliver from an order book, when computing
TER nodeDeliverRev (
RippleCalc&,
const unsigned int nodeIndex,
@@ -110,16 +135,6 @@ TER nodeDeliverFwd (
STAmount& saInAct,
STAmount& saInFees);
void nodeRipple (
RippleCalc&,
const std::uint32_t uQualityIn,
const std::uint32_t uQualityOut,
const STAmount& saPrvReq,
const STAmount& saCurReq,
STAmount& saPrvAct,
STAmount& saCurAct,
std::uint64_t& uRateMax);
} // path
} // ripple

View File

@@ -39,7 +39,7 @@ namespace path {
// do not need to push funds.
// - If next node is an offer and output is XRP then we need to deliver funds to
// limbo.
TER nodeAccountFwd (
TER computeForwardLiquidityForAccount (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, // 0 <= nodeIndex <= lastNodeIndex
PathState& pathState,
@@ -58,18 +58,18 @@ TER nodeAccountFwd (
const bool nextNodeIsAccount = nextNode.isAccount();
const uint160& previousAccountID
= previousNodeIsAccount ? previousNode.uAccountID : node.uAccountID;
= previousNodeIsAccount ? previousNode.account_ : node.account_;
// Offers are always issue.
const uint160& nextAccountID
= nextNodeIsAccount ? nextNode.uAccountID : node.uAccountID;
= nextNodeIsAccount ? nextNode.account_ : node.account_;
std::uint32_t uQualityIn = nodeIndex
? rippleCalc.mActiveLedger.rippleQualityIn (
node.uAccountID, previousAccountID, node.uCurrencyID)
node.account_, previousAccountID, node.currency_)
: QUALITY_ONE;
std::uint32_t uQualityOut = (nodeIndex == lastNodeIndex)
? rippleCalc.mActiveLedger.rippleQualityOut (
node.uAccountID, nextAccountID, node.uCurrencyID)
node.account_, nextAccountID, node.currency_)
: QUALITY_ONE;
// When looking backward (prv) for req we care about what we just
@@ -92,7 +92,7 @@ TER nodeAccountFwd (
previousNode.saFwdDeliver.getIssuer ());
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd> "
<< "computeForwardLiquidityForAccount> "
<< "nodeIndex=" << nodeIndex << "/" << lastNodeIndex
<< " previousNode.saFwdRedeem:" << previousNode.saFwdRedeem
<< " saPrvIssueReq:" << previousNode.saFwdIssue
@@ -139,7 +139,7 @@ TER nodeAccountFwd (
pathState.inPass() += node.saFwdIssue;
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: ^ --> ACCOUNT --> account :"
<< "computeForwardLiquidityForAccount: ^ --> ACCOUNT --> account :"
<< " saInReq=" << pathState.inReq()
<< " saInAct=" << pathState.inAct()
<< " node.saFwdRedeem:" << node.saFwdRedeem
@@ -151,11 +151,11 @@ TER nodeAccountFwd (
{
// account --> ACCOUNT --> $
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: account --> ACCOUNT --> $ :"
<< "computeForwardLiquidityForAccount: account --> ACCOUNT --> $ :"
<< " previousAccountID="
<< RippleAddress::createHumanAccountID (previousAccountID)
<< " node.uAccountID="
<< RippleAddress::createHumanAccountID (node.uAccountID)
<< " node.account_="
<< RippleAddress::createHumanAccountID (node.account_)
<< " previousNode.saFwdRedeem:" << previousNode.saFwdRedeem
<< " previousNode.saFwdIssue:" << previousNode.saFwdIssue;
@@ -177,7 +177,7 @@ TER nodeAccountFwd (
{
// Actually receive.
resultCode = rippleCalc.mActiveLedger.rippleCredit (
previousAccountID, node.uAccountID,
previousAccountID, node.account_,
previousNode.saFwdRedeem + previousNode.saFwdIssue, false);
}
else
@@ -190,7 +190,7 @@ TER nodeAccountFwd (
{
// account --> ACCOUNT --> account
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: account --> ACCOUNT --> account";
<< "computeForwardLiquidityForAccount: account --> ACCOUNT --> account";
node.saFwdRedeem.clear (node.saRevRedeem);
node.saFwdIssue.clear (node.saRevIssue);
@@ -200,7 +200,7 @@ TER nodeAccountFwd (
// Previous wants to redeem.
{
// Rate : 1.0 : quality out
nodeRipple (
computeRippleLiquidity (
rippleCalc,
QUALITY_ONE, uQualityOut, previousNode.saFwdRedeem, node.saRevRedeem,
saPrvRedeemAct, node.saFwdRedeem, uRateMax);
@@ -213,7 +213,7 @@ TER nodeAccountFwd (
// Current has more to redeem to next.
{
// Rate: quality in : quality out
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, uQualityOut, previousNode.saFwdIssue, node.saRevRedeem,
saPrvIssueAct, node.saFwdRedeem, uRateMax);
@@ -228,9 +228,9 @@ TER nodeAccountFwd (
// Current wants to issue.
{
// Rate : 1.0 : transfer_rate
nodeRipple (
computeRippleLiquidity (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
previousNode.saFwdRedeem, node.saRevIssue, saPrvRedeemAct,
node.saFwdIssue, uRateMax);
}
@@ -244,7 +244,7 @@ TER nodeAccountFwd (
// Current wants to issue.
{
// Rate: quality in : 1.0
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevIssue,
saPrvIssueAct, node.saFwdIssue, uRateMax);
@@ -255,7 +255,7 @@ TER nodeAccountFwd (
// Adjust prv --> cur balance : take all inbound
resultCode = saProvide
? rippleCalc.mActiveLedger.rippleCredit (
previousAccountID, node.uAccountID,
previousAccountID, node.account_,
previousNode.saFwdRedeem + previousNode.saFwdIssue, false)
: tecPATH_DRY;
}
@@ -272,7 +272,7 @@ TER nodeAccountFwd (
{
// Non-XRP, current node is the issuer.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: account --> ACCOUNT --> offer";
<< "computeForwardLiquidityForAccount: account --> ACCOUNT --> offer";
node.saFwdDeliver.clear (node.saRevDeliver);
@@ -285,9 +285,9 @@ TER nodeAccountFwd (
{
// Rate : 1.0 : transfer_rate
// XXX Is having the transfer rate here correct?
nodeRipple (
computeRippleLiquidity (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
previousNode.saFwdRedeem, node.saRevDeliver, saPrvRedeemAct,
node.saFwdDeliver, uRateMax);
}
@@ -299,7 +299,7 @@ TER nodeAccountFwd (
// Previous wants to issue. To next must be ok.
{
// Rate: quality in : 1.0
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevDeliver,
saPrvIssueAct, node.saFwdDeliver, uRateMax);
@@ -308,7 +308,7 @@ TER nodeAccountFwd (
// Adjust prv --> cur balance : take all inbound
resultCode = node.saFwdDeliver
? rippleCalc.mActiveLedger.rippleCredit (
previousAccountID, node.uAccountID,
previousAccountID, node.account_,
previousNode.saFwdRedeem + previousNode.saFwdIssue, false)
: tecPATH_DRY; // Didn't actually deliver anything.
}
@@ -325,11 +325,11 @@ TER nodeAccountFwd (
pathState.inReq() - pathState.inAct());
// Limit XRP by available. No limit for non-XRP as issuer.
if (node.uCurrencyID.isZero ())
if (node.currency_.isZero ())
node.saFwdDeliver = std::min (
node.saFwdDeliver,
rippleCalc.mActiveLedger.accountHolds (
node.uAccountID, CURRENCY_XRP, ACCOUNT_XRP));
node.account_, XRP_CURRENCY, XRP_ACCOUNT));
}
@@ -340,14 +340,14 @@ TER nodeAccountFwd (
{
resultCode = tecPATH_DRY;
}
else if (!!node.uCurrencyID)
else if (!!node.currency_)
{
// Non-XRP, current node is the issuer.
// We could be delivering to multiple accounts, so we don't know
// which ripple balance will be adjusted. Assume just issuing.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: ^ --> ACCOUNT -- !XRP --> offer";
<< "computeForwardLiquidityForAccount: ^ --> ACCOUNT -- !XRP --> offer";
// As the issuer, would only issue.
// Don't need to actually deliver. As from delivering leave in
@@ -356,11 +356,11 @@ TER nodeAccountFwd (
else
{
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: ^ --> ACCOUNT -- XRP --> offer";
<< "computeForwardLiquidityForAccount: ^ --> ACCOUNT -- XRP --> offer";
// Deliver XRP to limbo.
resultCode = rippleCalc.mActiveLedger.accountSend (
node.uAccountID, ACCOUNT_XRP, node.saFwdDeliver);
node.account_, XRP_ACCOUNT, node.saFwdDeliver);
}
}
}
@@ -370,7 +370,7 @@ TER nodeAccountFwd (
{
// offer --> ACCOUNT --> $
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: offer --> ACCOUNT --> $ : "
<< "computeForwardLiquidityForAccount: offer --> ACCOUNT --> $ : "
<< previousNode.saFwdDeliver;
STAmount& saCurReceive = pathState.outPass();
@@ -385,7 +385,7 @@ TER nodeAccountFwd (
{
// offer --> ACCOUNT --> account
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: offer --> ACCOUNT --> account";
<< "computeForwardLiquidityForAccount: offer --> ACCOUNT --> account";
node.saFwdRedeem.clear (node.saRevRedeem);
node.saFwdIssue.clear (node.saRevIssue);
@@ -395,7 +395,7 @@ TER nodeAccountFwd (
// Previous wants to deliver and can current redeem.
{
// Rate : 1.0 : quality out
nodeRipple (
computeRippleLiquidity (
rippleCalc,
QUALITY_ONE, uQualityOut, previousNode.saFwdDeliver, node.saRevRedeem,
saPrvDeliverAct, node.saFwdRedeem, uRateMax);
@@ -411,9 +411,9 @@ TER nodeAccountFwd (
// Current wants issue.
{
// Rate : 1.0 : transfer_rate
nodeRipple (
computeRippleLiquidity (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
previousNode.saFwdDeliver, node.saRevIssue, saPrvDeliverAct,
node.saFwdIssue, uRateMax);
}
@@ -431,7 +431,7 @@ TER nodeAccountFwd (
// offer --> ACCOUNT --> offer
// deliver/redeem -> deliver/issue.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountFwd: offer --> ACCOUNT --> offer";
<< "computeForwardLiquidityForAccount: offer --> ACCOUNT --> offer";
node.saFwdDeliver.clear (node.saRevDeliver);
@@ -441,9 +441,9 @@ TER nodeAccountFwd (
// Current wants issue.
{
// Rate : 1.0 : transfer_rate
nodeRipple (
computeRippleLiquidity (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
previousNode.saFwdDeliver, node.saRevDeliver, saPrvDeliverAct,
node.saFwdDeliver, uRateMax);
}

View File

@@ -25,19 +25,22 @@ namespace ripple {
namespace path {
// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur, based on
// required deliverable, propagate redeem, issue, and deliver requests to the
// previous node.
// required deliverable, propagate redeem, issue (for accounts) and deliver
// requests (for order books) to the previous node.
//
// Inflate amount requested by required fees.
// Reedems are limited based on IOUs previous has on hand.
// Issues are limited based on credit limits and amount owed.
//
// No account balance adjustments as we don't know how much is going to actually
// be pushed through yet.
// Currency cannot be XRP because we are rippling.
//
// No permanent account balance adjustments as we don't know how much is going
// to actually be pushed through yet - changes are only in the scratch pad
// ledger.
//
// <-- tesSUCCESS or tecPATH_DRY
TER nodeAccountRev (
TER computeReverseLiquidityForAccount (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, PathState& pathState,
const bool bMultiQuality)
@@ -46,6 +49,7 @@ TER nodeAccountRev (
auto const lastNodeIndex = pathState.nodes().size () - 1;
auto const isFinalNode = (nodeIndex == lastNodeIndex);
// 0 quality means none has yet been determined.
std::uint64_t uRateMax = 0;
auto& previousNode = pathState.nodes()[nodeIndex ? nodeIndex - 1 : 0];
@@ -57,82 +61,88 @@ TER nodeAccountRev (
const bool nextNodeIsAccount = isFinalNode || nextNode.isAccount();
const uint160& previousAccountID = previousNodeIsAccount
? previousNode.uAccountID : node.uAccountID;
const uint160& nextAccountID = nextNodeIsAccount ? nextNode.uAccountID
: node.uAccountID; // Offers are always issue.
? previousNode.account_ : node.account_;
const uint160& nextAccountID = nextNodeIsAccount ? nextNode.account_
: node.account_; // Offers are always issue.
// XXX Don't look up quality for XRP
const std::uint32_t uQualityIn = nodeIndex
? rippleCalc.mActiveLedger.rippleQualityIn (
node.uAccountID, previousAccountID, node.uCurrencyID)
: QUALITY_ONE;
const std::uint32_t uQualityOut = (nodeIndex != lastNodeIndex)
? rippleCalc.mActiveLedger.rippleQualityOut (
node.uAccountID, nextAccountID, node.uCurrencyID)
: QUALITY_ONE;
// This is the quality from from the previous node to this one.
const std::uint32_t uQualityIn
= (nodeIndex != 0)
? rippleCalc.mActiveLedger.rippleQualityIn (
node.account_, previousAccountID, node.currency_)
: QUALITY_ONE;
// And this is the quality from the next one to this one.
const std::uint32_t uQualityOut
= (nodeIndex != lastNodeIndex)
? rippleCalc.mActiveLedger.rippleQualityOut (
node.account_, nextAccountID, node.currency_)
: QUALITY_ONE;
// For previousNodeIsAccount:
// Previous account is owed.
const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex)
// Previous account is already owed.
const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex != 0)
? rippleCalc.mActiveLedger.rippleOwed (
node.uAccountID, previousAccountID, node.uCurrencyID)
: STAmount (node.uCurrencyID, node.uAccountID);
node.account_, previousAccountID, node.currency_)
: STAmount (node.currency_, node.account_);
// Previous account may owe.
const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex)
// The limit amount that the previous account may owe.
const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex != 0)
? rippleCalc.mActiveLedger.rippleLimit (
node.uAccountID, previousAccountID, node.uCurrencyID)
: STAmount (node.uCurrencyID, node.uAccountID);
node.account_, previousAccountID, node.currency_)
: STAmount (node.currency_, node.account_);
// Next account is owed.
const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex != lastNodeIndex)
? rippleCalc.mActiveLedger.rippleOwed (
node.uAccountID, nextAccountID, node.uCurrencyID)
: STAmount (node.uCurrencyID, node.uAccountID);
node.account_, nextAccountID, node.currency_)
: STAmount (node.currency_, node.account_);
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev>"
<< "computeReverseLiquidityForAccount>"
<< " nodeIndex=%d/%d" << nodeIndex << "/" << lastNodeIndex
<< " previousAccountID="
<< RippleAddress::createHumanAccountID (previousAccountID)
<< " node.uAccountID="
<< RippleAddress::createHumanAccountID (node.uAccountID)
<< " nextAccountID="
<< RippleAddress::createHumanAccountID (nextAccountID)
<< " uCurrencyID="
<< STAmount::createHumanCurrency (node.uCurrencyID)
<< " previousAccountID=" << previousAccountID
<< " node.account_=" << node.account_
<< " nextAccountID=" << nextAccountID
<< " currency_=" << node.currency_
<< " uQualityIn=" << uQualityIn
<< " uQualityOut=" << uQualityOut
<< " saPrvOwed=" << saPrvOwed
<< " saPrvLimit=" << saPrvLimit;
// Requests are computed to be the maximum flow possible.
// Previous can redeem the owed IOUs it holds.
const STAmount saPrvRedeemReq = (saPrvOwed > zero)
const STAmount saPrvRedeemReq = (saPrvOwed > zero)
? saPrvOwed
: STAmount (saPrvOwed.getCurrency (), saPrvOwed.getIssuer ());
// This is the amount we're actually going to be setting for the previous
// node.
STAmount& saPrvRedeemAct = previousNode.saRevRedeem;
// Previous can issue up to limit minus whatever portion of limit already
// used (not including redeemable amount).
const STAmount saPrvIssueReq = (saPrvOwed < zero)
// used (not including redeemable amount) - another "maximum flow".
const STAmount saPrvIssueReq = (saPrvOwed < zero)
? saPrvLimit + saPrvOwed : saPrvLimit;
STAmount& saPrvIssueAct = previousNode.saRevIssue;
// For !previousNodeIsAccount
// Precompute these values in case we have an order book.
auto deliverCurrency = previousNode.saRevDeliver.getCurrency ();
const STAmount saPrvDeliverReq (
const STAmount saPrvDeliverReq (
deliverCurrency, previousNode.saRevDeliver.getIssuer (), -1);
// Unlimited.
// Unlimited delivery.
STAmount& saPrvDeliverAct = previousNode.saRevDeliver;
// For nextNodeIsAccount
const STAmount& saCurRedeemReq = node.saRevRedeem;
// Set to zero, because we're trying to hit the previous node.
STAmount saCurRedeemAct (
saCurRedeemReq.getCurrency (), saCurRedeemReq.getIssuer ());
const STAmount& saCurIssueReq = node.saRevIssue;
// Track progress.
const STAmount& saCurIssueReq = node.saRevIssue;
// Track the amount we actually redeem.
STAmount saCurIssueAct (
saCurIssueReq.getCurrency (), saCurIssueReq.getIssuer ());
@@ -142,7 +152,7 @@ TER nodeAccountRev (
saCurDeliverReq.getCurrency (), saCurDeliverReq.getIssuer ());
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev:"
<< "computeReverseLiquidityForAccount:"
<< " saPrvRedeemReq:" << saPrvRedeemReq
<< " saPrvIssueReq:" << saPrvIssueReq
<< " saPrvDeliverAct:" << saPrvDeliverAct
@@ -162,31 +172,37 @@ TER nodeAccountRev (
|| -saNxtOwed == saCurRedeemReq);
// If issue req, then redeem req must consume all owed.
if (!nodeIndex)
if (nodeIndex == 0)
{
// ^ --> ACCOUNT --> account|offer
// Nothing to do, there is no previous to adjust.
//
// TODO(tom): we could have skipped all that setup and just left
// or even just never call this whole routine on nodeIndex = 0!
}
// The next four cases correspond to the table at the bottom of this Wiki
// page section: https://ripple.com/wiki/Transit_Fees#Implementation
else if (previousNodeIsAccount && nextNodeIsAccount)
{
if (isFinalNode)
{
// account --> ACCOUNT --> $
// Overall deliverable.
// If previous is an account, limit.
const STAmount saCurWantedReq = std::min (
pathState.outReq() - pathState.outAct(), saPrvLimit + saPrvOwed);
STAmount saCurWantedAct (
pathState.outReq() - pathState.outAct(),
saPrvLimit + saPrvOwed);
STAmount saCurWantedAct (
saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: account --> ACCOUNT --> $ :"
<< "computeReverseLiquidityForAccount: account --> ACCOUNT --> $ :"
<< " saCurWantedReq=" << saCurWantedReq;
// Calculate redeem
if (saPrvRedeemReq) // Previous has IOUs to redeem.
{
// Redeem at 1:1
// Redeem your own IOUs at 1:1
saCurWantedAct = std::min (saPrvRedeemReq, saCurWantedReq);
saPrvRedeemAct = saCurWantedAct;
@@ -194,7 +210,7 @@ TER nodeAccountRev (
uRateMax = STAmount::uRateOne;
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: Redeem at 1:1"
<< "computeReverseLiquidityForAccount: Redeem at 1:1"
<< " saPrvRedeemReq=" << saPrvRedeemReq
<< " (available) saPrvRedeemAct=" << saPrvRedeemAct
<< " uRateMax="
@@ -209,19 +225,20 @@ TER nodeAccountRev (
saPrvIssueAct.clear (saPrvIssueReq);
if (saCurWantedReq != saCurWantedAct // Need more.
&& saPrvIssueReq) // Will accept IOUs from prevous.
&& saPrvIssueReq) // Will accept IOUs from previous.
{
// Rate: quality in : 1.0
// If we previously redeemed and this has a poorer rate, this
// won't be included the current increment.
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq,
uQualityIn, QUALITY_ONE,
saPrvIssueReq, saCurWantedReq,
saPrvIssueAct, saCurWantedAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: Issuing: Rate: quality in : 1.0"
<< "computeReverseLiquidityForAccount: Issuing: Rate: quality in : 1.0"
<< " saPrvIssueAct:" << saPrvIssueAct
<< " saCurWantedAct:" << saCurWantedAct;
}
@@ -234,61 +251,71 @@ TER nodeAccountRev (
}
else
{
// ^|account --> ACCOUNT --> account
// Not final node.
// account --> ACCOUNT --> account
saPrvRedeemAct.clear (saPrvRedeemReq);
saPrvIssueAct.clear (saPrvIssueReq);
// redeem (part 1) -> redeem
if (saCurRedeemReq // Next wants IOUs redeemed.
&& saPrvRedeemReq) // Previous has IOUs to redeem.
if (saCurRedeemReq
// Next wants IOUs redeemed from current account.
&& saPrvRedeemReq)
// Previous has IOUs to redeem to the current account.
{
// Rate : 1.0 : quality out
nodeRipple (
// TODO(tom): add English.
// Rate : 1.0 : quality out - we must accept our own IOUs as 1:1.
computeRippleLiquidity (
rippleCalc,
QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq,
QUALITY_ONE, uQualityOut,
saPrvRedeemReq, saCurRedeemReq,
saPrvRedeemAct, saCurRedeemAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: Rate : 1.0 : quality out"
<< "computeReverseLiquidityForAccount: "
<< "Rate : 1.0 : quality out"
<< " saPrvRedeemAct:" << saPrvRedeemAct
<< " saCurRedeemAct:" << saCurRedeemAct;
}
// issue (part 1) -> redeem
if (saCurRedeemReq != saCurRedeemAct
// Next wants more IOUs redeemed.
// The current node has more IOUs to redeem.
&& saPrvRedeemAct == saPrvRedeemReq)
// Previous has no IOUs to redeem remaining.
// The previous node has no IOUs to redeem remaining, so issues.
{
// Rate: quality in : quality out
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq,
uQualityIn, uQualityOut,
saPrvIssueReq, saCurRedeemReq,
saPrvIssueAct, saCurRedeemAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: Rate: quality in : quality out:"
<< "computeReverseLiquidityForAccount: "
<< "Rate: quality in : quality out:"
<< " saPrvIssueAct:" << saPrvIssueAct
<< " saCurRedeemAct:" << saCurRedeemAct;
}
// redeem (part 2) -> issue.
if (saCurIssueReq // Next wants IOUs issued.
// TODO(tom): this condition seems redundant.
&& saCurRedeemAct == saCurRedeemReq
// Can only issue if completed redeeming.
&& saPrvRedeemAct != saPrvRedeemReq)
// Did not complete redeeming previous IOUs.
{
// Rate : 1.0 : transfer_rate
nodeRipple (
computeRippleLiquidity (
rippleCalc,
QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct,
saCurIssueAct, uRateMax);
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
saPrvRedeemReq, saCurIssueReq,
saPrvRedeemAct, saCurIssueAct, uRateMax);
WriteLog (lsDEBUG, RippleCalc)
<< "nodeAccountRev: Rate : 1.0 : transfer_rate:"
<< "computeReverseLiquidityForAccount: "
<< "Rate : 1.0 : transfer_rate:"
<< " saPrvRedeemAct:" << saPrvRedeemAct
<< " saCurIssueAct:" << saCurIssueAct;
}
@@ -304,13 +331,15 @@ TER nodeAccountRev (
// Previous can issue.
{
// Rate: quality in : 1.0
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq,
uQualityIn, QUALITY_ONE,
saPrvIssueReq, saCurIssueReq,
saPrvIssueAct, saCurIssueAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: Rate: quality in : 1.0:"
<< "computeReverseLiquidityForAccount: "
<< "Rate: quality in : 1.0:"
<< " saPrvIssueAct:" << saPrvIssueAct
<< " saCurIssueAct:" << saCurIssueAct;
}
@@ -318,11 +347,12 @@ TER nodeAccountRev (
if (!saCurRedeemAct && !saCurIssueAct)
{
// Did not make progress.
terResult = tecPATH_DRY;
terResult = tecPATH_DRY;
}
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: ^|account --> ACCOUNT --> account :"
<< "computeReverseLiquidityForAccount: "
<< "^|account --> ACCOUNT --> account :"
<< " saCurRedeemReq:" << saCurRedeemReq
<< " saCurIssueReq:" << saCurIssueReq
<< " saPrvOwed:" << saPrvOwed
@@ -336,31 +366,41 @@ TER nodeAccountRev (
// Note: deliver is always issue as ACCOUNT is the issuer for the offer
// input.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: account --> ACCOUNT --> offer";
<< "computeReverseLiquidityForAccount: "
<< "account --> ACCOUNT --> offer";
saPrvRedeemAct.clear (saPrvRedeemReq);
saPrvIssueAct.clear (saPrvIssueReq);
// We have three cases: the nxt offer can be owned by current account,
// previous account or some third party account.
//
// Also, the current account may or may not have a redeemable balance
// with the account for the next offer, so we don't yet know if we're
// redeeming or issuing.
//
// TODO(tom): Make sure deliver was cleared, or check actual is zero.
// redeem -> deliver/issue.
if (saPrvOwed > zero // Previous has IOUs to redeem.
&& saCurDeliverReq) // Need some issued.
{
// Rate : 1.0 : transfer_rate
nodeRipple (
computeRippleLiquidity (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct,
saCurDeliverAct, uRateMax);
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
saPrvRedeemReq, saCurDeliverReq,
saPrvRedeemAct, saCurDeliverAct, uRateMax);
}
// issue -> deliver/issue
if (saPrvRedeemReq == saPrvRedeemAct // Previously redeemed all owed.
if (saPrvRedeemReq == saPrvRedeemAct // Previously redeemed all owed.
&& saCurDeliverReq != saCurDeliverAct) // Still need some issued.
{
// Rate: quality in : 1.0
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq,
uQualityIn, QUALITY_ONE,
saPrvIssueReq, saCurDeliverReq,
saPrvIssueAct, saCurDeliverAct, uRateMax);
}
@@ -371,7 +411,7 @@ TER nodeAccountRev (
}
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: "
<< "computeReverseLiquidityForAccount: "
<< " saCurDeliverReq:" << saCurDeliverReq
<< " saCurDeliverAct:" << saCurDeliverAct
<< " saPrvOwed:" << saPrvOwed;
@@ -382,12 +422,16 @@ TER nodeAccountRev (
{
// offer --> ACCOUNT --> $
// Previous is an offer, no limit: redeem own IOUs.
const STAmount& saCurWantedReq = pathState.outReq() - pathState.outAct();
STAmount saCurWantedAct (
saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());
//
// This is the final node; we can't look to the right to get values;
// we have to go up to get the out value for the entire path state.
const STAmount& saCurWantedReq =
pathState.outReq() - pathState.outAct();
STAmount saCurWantedAct = saCurWantedReq.zeroed();
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: offer --> ACCOUNT --> $ :"
<< "computeReverseLiquidityForAccount: "
<< "offer --> ACCOUNT --> $ :"
<< " saCurWantedReq:" << saCurWantedReq
<< " saOutAct:" << pathState.outAct()
<< " saOutReq:" << pathState.outReq();
@@ -395,17 +439,34 @@ TER nodeAccountRev (
if (saCurWantedReq <= zero)
{
// TEMPORARY emergency fix
//
// TODO(tom): why can't saCurWantedReq be -1 if you want to
// compute maximum liquidity? This might be unimplemented
// functionality. TODO(tom): should the same check appear in
// other paths or even be pulled up?
WriteLog (lsFATAL, RippleCalc) << "CurWantReq was not positive";
return tefEXCEPTION;
}
assert (saCurWantedReq > zero); // FIXME: We got one of these
// TODO(tom): can only be a race condition if true!
// The previous node is an offer; we are receiving our own currency;
// The previous order book's entries might hold our issuances; might
// not hold our issuances; might be our own offer.
//
// Assume the worst case, the case which costs the most to go
// through, which is that it is not our own offer or our own
// issuances. Later on the forward pass we may be able to do
// better.
//
// TODO: this comment applies generally to this section - move it up
// to a document.
// Rate: quality in : 1.0
nodeRipple (
computeRippleLiquidity (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvDeliverReq, saCurWantedReq,
uQualityIn, QUALITY_ONE,
saPrvDeliverReq, saCurWantedReq,
saPrvDeliverAct, saCurWantedAct, uRateMax);
if (!saCurWantedAct)
@@ -415,7 +476,7 @@ TER nodeAccountRev (
}
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev:"
<< "computeReverseLiquidityForAccount:"
<< " saPrvDeliverAct:" << saPrvDeliverAct
<< " saPrvDeliverReq:" << saPrvDeliverReq
<< " saCurWantedAct:" << saCurWantedAct
@@ -426,17 +487,25 @@ TER nodeAccountRev (
// offer --> ACCOUNT --> account
// Note: offer is always delivering(redeeming) as account is issuer.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: offer --> ACCOUNT --> account :"
<< "computeReverseLiquidityForAccount: "
<< "offer --> ACCOUNT --> account :"
<< " saCurRedeemReq:" << saCurRedeemReq
<< " saCurIssueReq:" << saCurIssueReq;
// deliver -> redeem
// TODO(tom): now we have more checking in nodeRipple, these checks
// might be redundant.
if (saCurRedeemReq) // Next wants us to redeem.
{
// cur holds IOUs from the account to the right, the nxt
// account. If someone is making the current account get rid of
// the nxt account's IOUs, then charge the input for quality out.
//
// Rate : 1.0 : quality out
nodeRipple (
computeRippleLiquidity (
rippleCalc,
QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq,
QUALITY_ONE, uQualityOut,
saPrvDeliverReq, saCurRedeemReq,
saPrvDeliverAct, saCurRedeemAct, uRateMax);
}
@@ -447,16 +516,16 @@ TER nodeAccountRev (
// Need some issued.
{
// Rate : 1.0 : transfer_rate
nodeRipple (
computeRippleLiquidity (
rippleCalc,
QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct,
saCurIssueAct, uRateMax);
}
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev:"
<< "computeReverseLiquidityForAccount:"
<< " saCurRedeemAct:" << saCurRedeemAct
<< " saCurRedeemReq:" << saCurRedeemReq
<< " saPrvDeliverAct:" << saPrvDeliverAct
@@ -474,13 +543,13 @@ TER nodeAccountRev (
// offer --> ACCOUNT --> offer
// deliver/redeem -> deliver/issue.
WriteLog (lsTRACE, RippleCalc)
<< "nodeAccountRev: offer --> ACCOUNT --> offer";
<< "computeReverseLiquidityForAccount: offer --> ACCOUNT --> offer";
// Rate : 1.0 : transfer_rate
nodeRipple (
computeRippleLiquidity (
rippleCalc,
QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
rippleCalc.mActiveLedger.rippleTransferRate (node.account_),
saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct,
saCurDeliverAct, uRateMax);

View File

@@ -27,79 +27,94 @@
namespace ripple {
namespace path {
TER nodeFwd (
TER computeForwardLiqudity (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, PathState& pathState,
const bool bMultiQuality)
{
auto const& node = pathState.nodes()[nodeIndex];
auto const isAccount = node.isAccount();
WriteLog (lsTRACE, RippleCalc)
<< "nodeFwd> nodeIndex=" << nodeIndex;
<< "computeForwardLiqudity> nodeIndex=" << nodeIndex;
TER resultCode = isAccount
? nodeAccountFwd (rippleCalc, nodeIndex, pathState, bMultiQuality)
: nodeOfferFwd (rippleCalc, nodeIndex, pathState, bMultiQuality);
TER resultCode = node.isAccount()
? computeForwardLiquidityForAccount (
rippleCalc, nodeIndex, pathState, bMultiQuality)
: computeForwardLiquidityForOffer (
rippleCalc, nodeIndex, pathState, bMultiQuality);
if (resultCode == tesSUCCESS && nodeIndex + 1 != pathState.nodes().size ())
resultCode = nodeFwd (rippleCalc, nodeIndex + 1, pathState, bMultiQuality);
resultCode = computeForwardLiqudity (rippleCalc, nodeIndex + 1, pathState, bMultiQuality);
if (resultCode == tesSUCCESS && !(pathState.inPass() && pathState.outPass()))
resultCode = tecPATH_DRY;
WriteLog (lsTRACE, RippleCalc)
<< "nodeFwd<"
<< "computeForwardLiqudity<"
<< " nodeIndex:" << nodeIndex
<< " resultCode:" << resultCode;
return resultCode;
}
// Calculate a node and its previous nodes.
// Calculate a node and its previous nodes. The eventual goal is to determine
// how much input currency we need in the forward direction to satisfy the
// output.
//
// From the destination work in reverse towards the source calculating how much
// must be asked for.
// must be asked for. As we move backwards, individual nodes may further limit
// the amount of liquidity available.
//
// This is just a controlling loop that sets things up and then hands the work
// off to either computeReverseLiquidityForAccount or computeReverseLiquidityForOffer.
//
// Later on the result of this will be used to work forward, figuring out how
// much can actually be delivered.
//
// Then work forward, figuring out how much can actually be delivered.
// <-- resultCode: tesSUCCESS or tecPATH_DRY
// <-> pnNodes:
// --> [end]saWanted.mAmount
// --> [all]saWanted.mCurrency
// --> [all]saAccount
// <-> [0]saWanted.mAmount : --> limit, <-- actual
TER nodeRev (
TER computeReverseLiqudity (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, PathState& pathState,
const bool bMultiQuality)
{
auto& node = pathState.nodes()[nodeIndex];
auto const isAccount = node.isAccount();
TER resultCode;
node.saTransferRate = STAmount::saFromRate (
rippleCalc.mActiveLedger.rippleTransferRate (node.uIssuerID));
// Every account has a transfer rate for its issuances.
// TOMOVE: The account charges
// a fee when third parties transfer that account's own issuances.
// node.transferRate_ caches the output transfer rate for this node.
node.transferRate_ = STAmount::saFromRate (
rippleCalc.mActiveLedger.rippleTransferRate (node.issuer_));
WriteLog (lsTRACE, RippleCalc)
<< "nodeRev>"
<< "computeReverseLiqudity>"
<< " nodeIndex=" << nodeIndex
<< " isAccount=" << isAccount
<< " uIssuerID=" << RippleAddress::createHumanAccountID (node.uIssuerID)
<< " saTransferRate=" << node.saTransferRate;
<< " issuer_=" << RippleAddress::createHumanAccountID (node.issuer_)
<< " transferRate_=" << node.transferRate_;
resultCode = isAccount
? nodeAccountRev (rippleCalc, nodeIndex, pathState, bMultiQuality)
: nodeOfferRev (rippleCalc, nodeIndex, pathState, bMultiQuality);
auto resultCode = node.isAccount()
? computeReverseLiquidityForAccount (
rippleCalc, nodeIndex, pathState, bMultiQuality)
: computeReverseLiquidityForOffer (
rippleCalc, nodeIndex, pathState, bMultiQuality);
// Do previous.
if (resultCode == tesSUCCESS && nodeIndex)
{
// Continue in reverse. TODO(tom): remove unnecessary recursion.
resultCode = nodeRev (rippleCalc, nodeIndex - 1, pathState, bMultiQuality);
resultCode = computeReverseLiqudity (
rippleCalc, nodeIndex - 1, pathState, bMultiQuality);
}
WriteLog (lsTRACE, RippleCalc)
<< "nodeRev< "
<< "computeReverseLiqudity< "
<< "nodeIndex=" << nodeIndex
<< " resultCode=%s" << transToken (resultCode)
<< "/" << resultCode;

View File

@@ -0,0 +1,111 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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/module/app/paths/Calculators.h>
#include <ripple/module/app/paths/RippleCalc.h>
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// Called to drive the from the first offer node in a chain.
//
// - Offer input is in issuer/limbo.
// - Current offers consumed.
// - Current offer owners debited.
// - Transfer fees credited to issuer.
// - Payout to issuer or limbo.
// - Deliver is set without transfer fees.
TER computeForwardLiquidityForOffer (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
const bool bMultiQuality)
{
auto& previousNode = pathState.nodes() [nodeIndex - 1];
if (previousNode.account_ == zero)
return tesSUCCESS;
// Previous is an account node, resolve its deliver.
STAmount saInAct;
STAmount saInFees;
auto resultCode = nodeDeliverFwd (
rippleCalc,
nodeIndex,
pathState,
bMultiQuality,
previousNode.account_,
previousNode.saFwdDeliver, // Previous is sending this much.
saInAct,
saInFees);
assert (resultCode != tesSUCCESS ||
previousNode.saFwdDeliver == saInAct + saInFees);
return resultCode;
}
// Called to drive from the last offer node in a chain.
// nodeIndex doesn't refer to the node at either end because both ends must be
// an account.
TER computeReverseLiquidityForOffer (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
const bool bMultiQuality)
{
auto& nextNode = pathState.nodes() [nodeIndex + 1];
if (nextNode.account_ == zero)
{
WriteLog (lsTRACE, RippleCalc)
<< "computeReverseLiquidityForOffer: "
<< "OFFER --> offer: nodeIndex=" << nodeIndex;
return tesSUCCESS;
// This control structure ensures nodeDeliverRev is only called for the
// rightmost offer in a chain of offers - which means that
// nodeDeliverRev has to take all of those offers into consideration.
}
auto& node = pathState.nodes() [nodeIndex];
// Next is an account node, resolve current offer node's deliver.
STAmount saDeliverAct;
WriteLog (lsTRACE, RippleCalc)
<< "computeReverseLiquidityForOffer: OFFER --> account:"
<< " nodeIndex=" << nodeIndex
<< " saRevDeliver=" << node.saRevDeliver;
return nodeDeliverRev (
rippleCalc,
nodeIndex,
pathState,
bMultiQuality,
nextNode.account_,
// The next node wants the current node to deliver this much:
node.saRevDeliver,
saDeliverAct);
}
} // path
} // ripple

View File

@@ -35,29 +35,29 @@ namespace path {
// to the sender, moving forward it propagates the actual send toward the
// receiver.
//
// This routine works backwards:
// - cur is the driver: it calculates previous wants based on previous credit
// limits and current wants.
// When this routine works backwards, saCurReq is the driving variable: it
// calculates previous wants based on previous credit limits and current wants.
//
// This routine works forwards:
// - prv is the driver: it calculates current deliver based on previous delivery
// limits and current wants.
// When this routine works forwards, saPrvReq is the driving variable: it
// calculates current deliver based on previous delivery limits and current
// wants.
//
// This routine is called one or two times for a node in a pass. If called once,
// it will work and set a rate. If called again, the new work must not worsen
// the previous rate.
void nodeRipple (
void computeRippleLiquidity (
RippleCalc& rippleCalc,
const std::uint32_t uQualityIn,
const std::uint32_t uQualityOut,
const STAmount& saPrvReq, // --> in limit including fees, <0 = unlimited
const STAmount& saCurReq, // --> out limit (driver)
const STAmount& saCurReq, // --> out limit
STAmount& saPrvAct, // <-> in limit including achieved so far: <-- <= -->
STAmount& saCurAct, // <-> out limit including achieved : <-- <= -->
STAmount& saCurAct, // <-> out limit including achieved so far: <-- <= -->
std::uint64_t& uRateMax)
{
WriteLog (lsTRACE, RippleCalc)
<< "nodeRipple>"
<< "computeRippleLiquidity>"
<< " uQualityIn=" << uQualityIn
<< " uQualityOut=" << uQualityOut
<< " saPrvReq=" << saPrvReq
@@ -65,31 +65,45 @@ void nodeRipple (
<< " saPrvAct=" << saPrvAct
<< " saCurAct=" << saCurAct;
assert (saCurReq > zero); // FIXME: saCurReq was zero
// saCurAct was once zero in a production server.
assert (saCurReq != zero);
assert (saCurReq > zero);
assert (saPrvReq.getCurrency () == saCurReq.getCurrency ());
assert (saPrvReq.getCurrency () == saPrvAct.getCurrency ());
assert (saPrvReq.getIssuer () == saPrvAct.getIssuer ());
const bool bPrvUnlimited = saPrvReq < zero;
const STAmount saPrv = bPrvUnlimited ? STAmount (saPrvReq)
: saPrvReq - saPrvAct;
const STAmount saCur = saCurReq - saCurAct;
const bool bPrvUnlimited = (saPrvReq < zero); // -1 means unlimited.
// Unlimited stays unlimited - don't do calculations.
// How much could possibly flow through the previous node?
const STAmount saPrv = bPrvUnlimited ? saPrvReq : saPrvReq - saPrvAct;
// How much could possibly flow through the current node?
const STAmount saCur = saCurReq - saCurAct;
WriteLog (lsTRACE, RippleCalc)
<< "nodeRipple: "
<< "computeRippleLiquidity: "
<< " bPrvUnlimited=" << bPrvUnlimited
<< " saPrv=" << saPrv
<< " saCur=" << saCur;
// If nothing can flow, we might as well not do any work.
if (saPrv == zero || saCur == zero)
return;
if (uQualityIn >= uQualityOut)
{
// No fee.
WriteLog (lsTRACE, RippleCalc) << "nodeRipple: No fees";
// You're getting better quality than you asked for, so no fee.
WriteLog (lsTRACE, RippleCalc) << "computeRippleLiquidity: No fees";
// Only process if we are not worsing previously processed.
// Only process if the current rate, 1:1, is not worse than the previous
// rate, uRateMax - otherwise there is no flow.
if (!uRateMax || STAmount::uRateOne <= uRateMax)
{
// Limit amount to transfer if need.
// Limit amount to transfer if need - the minimum of amount being
// paid and the amount that's wanted.
STAmount saTransfer = bPrvUnlimited ? saCur
: std::min (saPrv, saCur);
@@ -98,73 +112,84 @@ void nodeRipple (
//
// In forward, we want to propagate the limited prv to cur and set
// actual prv.
//
// This is the actual flow.
saPrvAct += saTransfer;
saCurAct += saTransfer;
// If no rate limit, set rate limit to avoid combining with
// something with a worse rate.
if (!uRateMax)
if (uRateMax == 0)
uRateMax = STAmount::uRateOne;
}
}
else
{
// Fee.
WriteLog (lsTRACE, RippleCalc) << "nodeRipple: Fee";
// If the quality is worse than the previous
WriteLog (lsTRACE, RippleCalc) << "computeRippleLiquidity: Fee";
std::uint64_t uRate = STAmount::getRate (
STAmount (uQualityOut), STAmount (uQualityIn));
// If the next rate is at least as good as the current rate, process.
if (!uRateMax || uRate <= uRateMax)
{
const uint160 uCurrencyID = saCur.getCurrency ();
const uint160 currency = saCur.getCurrency ();
const uint160 uCurIssuerID = saCur.getIssuer ();
// TODO(tom): what's this?
auto someFee = STAmount::mulRound (
saCur, uQualityOut, uCurrencyID, uCurIssuerID, true);
// current actual = current request * (quality out / quality in).
auto numerator = STAmount::mulRound (
saCur, uQualityOut, currency, uCurIssuerID, true);
// True means "round up" to get best flow.
STAmount saCurIn = STAmount::divRound (
someFee, uQualityIn, uCurrencyID, uCurIssuerID, true);
numerator, uQualityIn, currency, uCurIssuerID, true);
WriteLog (lsTRACE, RippleCalc)
<< "nodeRipple:"
<< "computeRippleLiquidity:"
<< " bPrvUnlimited=" << bPrvUnlimited
<< " saPrv=" << saPrv
<< " saCurIn=" << saCurIn;
if (bPrvUnlimited || saCurIn <= saPrv)
{
// All of cur. Some amount of prv.
// All of current. Some amount of previous.
saCurAct += saCur;
saPrvAct += saCurIn;
WriteLog (lsTRACE, RippleCalc)
<< "nodeRipple:3c:"
<< "computeRippleLiquidity:3c:"
<< " saCurReq=" << saCurReq
<< " saPrvAct=" << saPrvAct;
}
else
{
// TODO(tom): what's this?
auto someFee = STAmount::mulRound (
saPrv, uQualityIn, uCurrencyID, uCurIssuerID, true);
// A part of cur. All of prv. (cur as driver)
STAmount saCurOut = STAmount::divRound (
someFee, uQualityOut, uCurrencyID, uCurIssuerID, true);
// There wasn't enough money to start with - so given the
// limited input, how much could we deliver?
// current actual = previous request
// * (quality in / quality out).
// This is inverted compared to the code above because we're
// going the other way
auto numerator = STAmount::mulRound (
saPrv, uQualityIn, currency, uCurIssuerID, true);
// A part of current. All of previous. (Cur is the driver
// variable.)
STAmount saCurOut = STAmount::divRound (
numerator, uQualityOut, currency, uCurIssuerID, true);
WriteLog (lsTRACE, RippleCalc)
<< "nodeRipple:4: saCurReq=" << saCurReq;
saCurAct += saCurOut;
saPrvAct = saPrvReq;
<< "computeRippleLiquidity:4: saCurReq=" << saCurReq;
saCurAct += saCurOut;
saPrvAct = saPrvReq;
}
if (!uRateMax)
uRateMax = uRate;
uRateMax = uRate;
}
}
WriteLog (lsTRACE, RippleCalc)
<< "nodeRipple<"
<< "computeRippleLiquidity<"
<< " uQualityIn=" << uQualityIn
<< " uQualityOut=" << uQualityOut
<< " saPrvReq=" << saPrvReq

View File

@@ -24,12 +24,13 @@ namespace path {
bool Node::operator== (const Node& other) const
{
return other.uFlags == uFlags
&& other.uAccountID == uAccountID
&& other.uCurrencyID == uCurrencyID
&& other.uIssuerID == uIssuerID;
&& other.account_ == account_
&& other.currency_ == currency_
&& other.issuer_ == issuer_;
}
// This is for debugging not end users. Output names can be changed without warning.
// This is for debugging not end users. Output names can be changed without
// warning.
Json::Value Node::getJson () const
{
Json::Value jvNode (Json::objectValue);
@@ -37,9 +38,9 @@ Json::Value Node::getJson () const
jvNode["type"] = uFlags;
bool const hasCurrency (uCurrencyID != zero);
bool const hasAccount (uAccountID != zero);
bool const hasIssuer (uIssuerID != zero);
bool const hasCurrency = (currency_ != zero);
bool const hasAccount = (account_ != zero);
bool const hasIssuer = (issuer_ != zero);
if (isAccount() || hasAccount)
{
@@ -62,14 +63,14 @@ Json::Value Node::getJson () const
jvNode["flags"] = jvFlags;
if (!!uAccountID)
jvNode["account"] = RippleAddress::createHumanAccountID (uAccountID);
if (!!account_)
jvNode["account"] = RippleAddress::createHumanAccountID (account_);
if (!!uCurrencyID)
jvNode["currency"] = STAmount::createHumanCurrency (uCurrencyID);
if (!!currency_)
jvNode["currency"] = STAmount::createHumanCurrency (currency_);
if (!!uIssuerID)
jvNode["issuer"] = RippleAddress::createHumanAccountID (uIssuerID);
if (!!issuer_)
jvNode["issuer"] = RippleAddress::createHumanAccountID (issuer_);
if (saRevRedeem)
jvNode["rev_redeem"] = saRevRedeem.getFullText ();

View File

@@ -20,6 +20,8 @@
#ifndef RIPPLE_APP_PATH_NODE_H
#define RIPPLE_APP_PATH_NODE_H
#include <ripple/module/app/paths/Types.h>
namespace ripple {
namespace path {
@@ -36,27 +38,29 @@ struct Node
bool operator == (Node const&) const;
std::uint16_t uFlags; // --> From path.
std::uint16_t uFlags; // --> From path.
uint160 uAccountID; // --> Accounts: Recieving/sending account.
uint160 uCurrencyID; // --> Accounts: Receive and send, Offers: send.
Account account_; // --> Accounts: Receiving/sending account.
Currency currency_; // --> Accounts: Receive and send, Offers: send.
// --- For offer's next has currency out.
uint160 uIssuerID; // --> Currency's issuer
Account issuer_; // --> Currency's issuer
STAmount saTransferRate; // Transfer rate for uIssuerID.
STAmount transferRate_; // Transfer rate for issuer.
// Computed by Reverse.
STAmount saRevRedeem; // <-- Amount to redeem to next.
STAmount saRevIssue; // <-- Amount to issue to next, limited by
// credit and outstanding IOUs. Issue
// isn't used by offers.
STAmount saRevDeliver; // <-- Amount to deliver to next regardless of fee.
STAmount saRevDeliver; // <-- Amount to deliver to next regardless of
// fee.
// Computed by forward.
STAmount saFwdRedeem; // <-- Amount node will redeem to next.
STAmount saFwdIssue; // <-- Amount node will issue to next.
// Issue isn't used by offers.
STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of fee.
STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of
// fee.
// For offers:
@@ -70,8 +74,16 @@ struct Node
// The "directories" are ordered in "increasing" "quality" value, which
// means that the first "directory" has the "best" (i.e. numerically least)
// "quality".
uint256 uDirectTip; // Current directory.
uint256 uDirectEnd; // Next order book.
// https://ripple.com/wiki/Ledger_Format#Prioritizing_a_continuous_key_space
// Current directory - the last 64 bits of this are the quality.
uint256 uDirectTip;
// Start of the next order book - one past the worst quality possible for
// the current order book.
uint256 uDirectEnd;
bool bDirectAdvance; // Need to advance directory.
bool bDirectRestart; // Need to restart directory.
SLE::pointer sleDirectDir;
@@ -80,10 +92,12 @@ struct Node
// PaymentNode
bool bEntryAdvance; // Need to advance entry.
unsigned int uEntry;
uint256 uOfferIndex;
uint256 offerIndex_;
SLE::pointer sleOffer;
uint160 uOfrOwnerID;
bool bFundsDirty; // Need to refresh saOfferFunds, saTakerPays, & saTakerGets.
Account offerOwnerAccount_;
// Do we need to refresh saOfferFunds, saTakerPays, & saTakerGets?
bool bFundsDirty;
STAmount saOfferFunds;
STAmount saTakerPays;
STAmount saTakerGets;

View File

@@ -31,6 +31,9 @@ namespace path {
// the best quality.
//
// <-- pathState.uQuality
//
// This is the wrapper that restores a checkpointed version of the ledger so we
// can write all over it without consequence.
void pathNext (
RippleCalc& rippleCalc,
@@ -59,7 +62,7 @@ void pathNext (
node.saFwdDeliver.clear ();
}
pathState.setStatus(nodeRev (rippleCalc, lastNodeIndex, pathState, bMultiQuality));
pathState.setStatus(computeReverseLiqudity (rippleCalc, lastNodeIndex, pathState, bMultiQuality));
WriteLog (lsTRACE, RippleCalc)
<< "pathNext: Path after reverse: " << pathState.getJson ();
@@ -69,13 +72,13 @@ void pathNext (
// Do forward.
lesCurrent = lesCheckpoint.duplicate (); // Restore from checkpoint.
pathState.setStatus(nodeFwd (rippleCalc, 0, pathState, bMultiQuality));
pathState.setStatus(computeForwardLiqudity (rippleCalc, 0, pathState, bMultiQuality));
}
if (tesSUCCESS == pathState.status())
{
CondLog (!pathState.inPass() || !pathState.outPass(), lsDEBUG, RippleCalc)
<< "pathNext: Error nodeFwd reported success for nothing:"
<< "pathNext: Error computeForwardLiqudity reported success for nothing:"
<< " saOutPass=" << pathState.outPass()
<< " inPass()=" << pathState.inPass();

View File

@@ -355,7 +355,7 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
if (!sameAccount || (c != saDstAmount.getCurrency ()))
{
if (c.isZero ())
sourceCurrencies.insert (std::make_pair (c, ACCOUNT_XRP));
sourceCurrencies.insert (std::make_pair (c, XRP_ACCOUNT));
else
sourceCurrencies.insert (std::make_pair (c, raSrcAccount.getAccountID ()));
}
@@ -426,7 +426,8 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
STAmount saDstAmountAct;
STAmount saMaxAmount (currIssuer.first,
currIssuer.second.isNonZero () ? currIssuer.second :
(currIssuer.first.isZero () ? ACCOUNT_XRP : raSrcAccount.getAccountID ()), 1);
(currIssuer.first.isZero () ? ACCOUNT_XRP :
raSrcAccount.getAccountID ()), 1);
saMaxAmount.negate ();
m_journal.debug << iIdentifier << " Paths found, calling rippleCalc";
TER resultCode = path::rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,

View File

@@ -25,16 +25,10 @@ namespace ripple {
class RippleCalc; // for logging
std::size_t hash_value (const AccountCurrencyIssuer& asValue)
{
std::size_t const seed = 0;
return beast::hardened_hash<AccountCurrencyIssuer>{seed}(asValue);
}
void PathState::clear() {
allLiquidityConsumed_ = false;
saInPass = zeroed (saInReq);
saOutPass = zeroed (saOutReq);
saInPass = saInReq.zeroed();
saOutPass = saOutReq.zeroed();
vUnfundedBecame.clear ();
umReverse.clear ();
}
@@ -54,7 +48,7 @@ bool PathState::lessPriority (PathState& lhs, PathState& rhs)
return lhs.mIndex > rhs.mIndex; // Bigger is worse.
}
// Make sure last path node delivers to uAccountID: uCurrencyID from uIssuerID.
// Make sure last path node delivers to account_: currency_ from issuer_.
//
// If the unadded next node as specified by arguments would not work as is, then
// add the necessary nodes so it would work.
@@ -69,56 +63,54 @@ bool PathState::lessPriority (PathState& lhs, PathState& rhs)
// - Offers can only go directly to another offer if the currency and issuer are
// an exact match.
// - Real issuers must be specified for non-XRP.
TER PathState::pushImply (
const uint160& uAccountID, // --> Delivering to this account.
const uint160& uCurrencyID, // --> Delivering this currency.
const uint160& uIssuerID) // --> Delivering this issuer.
TER PathState::pushImpliedNodes (
Account const& account, // --> Delivering to this account.
Currency const& currency, // --> Delivering this currency.
Account const& issuer) // --> Delivering this issuer.
{
auto const& previousNode = nodes_.back ();
TER resultCode = tesSUCCESS;
TER resultCode = tesSUCCESS;
WriteLog (lsTRACE, RippleCalc) << "pushImply>" <<
" " << RippleAddress::createHumanAccountID (uAccountID) <<
" " << STAmount::createHumanCurrency (uCurrencyID) <<
" " << RippleAddress::createHumanAccountID (uIssuerID);
WriteLog (lsTRACE, RippleCalc) << "pushImpliedNodes>" <<
" " << account <<
" " << currency <<
" " << issuer;
if (previousNode.uCurrencyID != uCurrencyID)
if (nodes_.back ().currency_ != currency)
{
// Currency is different, need to convert via an offer.
// Currency is different, need to convert via an offer from an order
// book. ACCOUNT_XRP does double duty as signaling "this is an order
// book. XRP_ACCOUNT does double duty as signaling "this is an order
// book".
// Corresponds to "Implies an offer directory" in the diagram, currently
// at https://docs.google.com/a/ripple.com/document/d/1b1RC8pKIgVZqUmjf9MW4IYxvzU7cBla4-pCSBbV4u8Q/edit
// at http://goo.gl/Uj3HAB.
resultCode = pushNode ( // Offer.
!!uCurrencyID
? STPathElement::typeCurrency | STPathElement::typeIssuer
: STPathElement::typeCurrency,
ACCOUNT_XRP, // Placeholder for offers.
uCurrencyID, // The offer's output is what is now wanted.
uIssuerID);
auto type = isXRP(currency) ? STPathElement::typeCurrency
: STPathElement::typeCurrency | STPathElement::typeIssuer;
// The offer's output is what is now wanted.
// XRP_ACCOUNT is a placeholder for offers.
resultCode = pushNode (type, XRP_ACCOUNT, currency, issuer);
}
auto const& pnBck = nodes_.back ();
// For ripple, non-XRP, ensure the issuer is on at least one side of the transaction.
// For ripple, non-XRP, ensure the issuer is on at least one side of the
// transaction.
if (resultCode == tesSUCCESS
&& !!uCurrencyID // Not XRP.
&& (pnBck.uAccountID != uIssuerID // Previous is not issuing own IOUs.
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
&& !isXRP(currency)
&& nodes_.back ().account_ != issuer
// Previous is not issuing own IOUs.
&& account != issuer)
// Current is not receiving own IOUs.
{
// Need to ripple through uIssuerID's account.
// Case "Implies an another node: (pushImply)" in the document.
resultCode = pushNode (
STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer,
uIssuerID, // Intermediate account is the needed issuer.
uCurrencyID,
uIssuerID);
// Need to ripple through issuer's account.
// Case "Implies an another node: (pushImpliedNodes)" in the document.
// Intermediate account is the needed issuer.
resultCode = pushNode (
STPathElement::typeAll, issuer, currency, issuer);
}
WriteLog (lsTRACE, RippleCalc) << "pushImply< : " << transToken (resultCode);
WriteLog (lsTRACE, RippleCalc)
<< "pushImpliedNodes< : " << transToken (resultCode);
return resultCode;
}
@@ -128,124 +120,129 @@ TER PathState::pushImply (
//
// For each non-matching pair of IssuedCurrency, there's an order book.
//
// <-- resultCode: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
// <-- resultCode: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH,
// terNO_LINE, tecPATH_DRY
TER PathState::pushNode (
const int iType,
const uint160& uAccountID, // If not specified, means an order book.
const uint160& uCurrencyID, // If not specified, default to previous.
const uint160& uIssuerID) // If not specified, default to previous.
Account const& account, // If not specified, means an order book.
Currency const& currency, // If not specified, default to previous.
Account const& issuer) // If not specified, default to previous.
{
path::Node node;
const bool bFirst = nodes_.empty ();
auto const& previousNode = bFirst ? path::Node () : nodes_.back ();
const bool pathIsEmpty = nodes_.empty ();
// TODO(tom): if pathIsEmpty, we probably don't need to do ANYTHING below.
// Indeed, we might just not even call pushNode in the first place!
auto const& previousNode = pathIsEmpty ? path::Node () : nodes_.back ();
// true, iff node is a ripple account. false, iff node is an offer node.
const bool bAccount (iType & STPathElement::typeAccount);
const bool hasAccount = (iType & STPathElement::typeAccount);
// Is currency specified for the output of the current node?
const bool bCurrency (iType & STPathElement::typeCurrency);
const bool hasCurrency = (iType & STPathElement::typeCurrency);
// Issuer is specified for the output of the current node.
const bool bIssuer (iType & STPathElement::typeIssuer);
const bool hasIssuer = (iType & STPathElement::typeIssuer);
TER resultCode = tesSUCCESS;
TER resultCode = tesSUCCESS;
WriteLog (lsTRACE, RippleCalc) << "pushNode> " <<
iType <<
": " << (bAccount ? RippleAddress::createHumanAccountID (uAccountID) : "-") <<
" " << (bCurrency ? STAmount::createHumanCurrency (uCurrencyID) : "-") <<
"/" << (bIssuer ? RippleAddress::createHumanAccountID (uIssuerID) : "-");
WriteLog (lsTRACE, RippleCalc)
<< "pushNode> " << iType << ": "
<< (hasAccount ? to_string(account) : std::string("-")) << " "
<< (hasCurrency ? to_string(currency) : std::string("-")) << "/"
<< (hasIssuer ? to_string(issuer) : std::string("-")) << "/";
node.uFlags = iType;
node.uCurrencyID = bCurrency ? uCurrencyID : previousNode.uCurrencyID;
node.currency_ = hasCurrency ? currency : Currency(previousNode.currency_);
if (iType & ~STPathElement::typeValidBits)
// TODO(tom): we can probably just return immediately whenever we hit an
// error in these next pages.
if (iType & ~STPathElement::typeAll)
{
// Of course, this could never happen.
WriteLog (lsDEBUG, RippleCalc) << "pushNode: bad bits.";
resultCode = temBAD_PATH;
}
else if (bIssuer && !node.uCurrencyID)
else if (hasIssuer && !node.currency_)
{
WriteLog (lsDEBUG, RippleCalc) << "pushNode: issuer specified for XRP.";
resultCode = temBAD_PATH;
}
else if (bIssuer && !uIssuerID)
else if (hasIssuer && !issuer)
{
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad issuer.";
resultCode = temBAD_PATH;
}
else if (!bAccount && !bCurrency && !bIssuer)
else if (!hasAccount && !hasCurrency && !hasIssuer)
{
// You can't default everything to the previous node as you would make
// no progress.
WriteLog (lsDEBUG, RippleCalc) << "pushNode: offer must specify at least currency or issuer.";
WriteLog (lsDEBUG, RippleCalc)
<< "pushNode: offer must specify at least currency or issuer.";
resultCode = temBAD_PATH;
}
else if (bAccount)
else if (hasAccount)
{
// Account link
node.uAccountID = uAccountID;
node.uIssuerID = bIssuer
? uIssuerID
: !!node.uCurrencyID // Not XRP.
? uAccountID
: ACCOUNT_XRP;
node.account_ = account;
node.issuer_ = hasIssuer
? issuer
: !!node.currency_ // Not XRP.
? account
: XRP_ACCOUNT;
// Zero value - for accounts.
node.saRevRedeem = STAmount (node.uCurrencyID, uAccountID);
node.saRevRedeem = STAmount (node.currency_, account);
node.saRevIssue = node.saRevRedeem;
// For order books only - zero currency with the issuer ID.
node.saRevDeliver = STAmount (node.uCurrencyID, node.uIssuerID);
node.saRevDeliver = STAmount (node.currency_, node.issuer_);
node.saFwdDeliver = node.saRevDeliver;
if (bFirst)
if (pathIsEmpty)
{
// The first node is always correct as is.
}
else if (!uAccountID)
else if (!account)
{
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad account.";
WriteLog (lsDEBUG, RippleCalc)
<< "pushNode: specified bad account.";
resultCode = temBAD_PATH;
}
else
{
// Add required intermediate nodes to deliver to current account.
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for account.";
WriteLog (lsTRACE, RippleCalc)
<< "pushNode: imply for account.";
resultCode = pushImply (
node.uAccountID, // Current account.
node.uCurrencyID, // Wanted currency.
!!node.uCurrencyID ? uAccountID : ACCOUNT_XRP); // Account as wanted issuer.
resultCode = pushImpliedNodes (
node.account_, node.currency_,
isXRP(node.currency_) ? XRP_ACCOUNT : account);
// Note: previousNode may no longer be the immediately previous node.
}
if (resultCode == tesSUCCESS && !nodes_.empty ())
{
auto const& pnBck = nodes_.back ();
bool bBckAccount = pnBck.isAccount();
if (bBckAccount)
auto const& backNode = nodes_.back ();
if (backNode.isAccount())
{
SLE::pointer sleRippleState = lesEntries.entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (pnBck.uAccountID, node.uAccountID, pnBck.uCurrencyID));
auto sleRippleState = lesEntries.entryCache (
ltRIPPLE_STATE,
Ledger::getRippleStateIndex (
backNode.account_, node.account_, backNode.currency_));
// A "RippleState" means a balance betweeen two accounts for a
// specific currency.
if (!sleRippleState)
{
WriteLog (lsTRACE, RippleCalc) << "pushNode: No credit line between "
<< RippleAddress::createHumanAccountID (pnBck.uAccountID)
<< " and "
<< RippleAddress::createHumanAccountID (node.uAccountID)
<< " for "
<< STAmount::createHumanCurrency (node.uCurrencyID)
<< "." ;
WriteLog (lsTRACE, RippleCalc)
<< "pushNode: No credit line between "
<< backNode.account_ << " and " << node.account_
<< " for " << node.currency_ << "." ;
WriteLog (lsTRACE, RippleCalc) << getJson ();
@@ -253,47 +250,56 @@ TER PathState::pushNode (
}
else
{
WriteLog (lsTRACE, RippleCalc) << "pushNode: Credit line found between "
<< RippleAddress::createHumanAccountID (pnBck.uAccountID)
<< " and "
<< RippleAddress::createHumanAccountID (node.uAccountID)
<< " for "
<< STAmount::createHumanCurrency (node.uCurrencyID)
<< "." ;
WriteLog (lsTRACE, RippleCalc)
<< "pushNode: Credit line found between "
<< backNode.account_ << " and " << node.account_
<< " for " << node.currency_ << "." ;
SLE::pointer sleBck = lesEntries.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (pnBck.uAccountID));
auto sleBck = lesEntries.entryCache (
ltACCOUNT_ROOT,
Ledger::getAccountRootIndex (backNode.account_));
// Is the source account the highest numbered account ID?
bool bHigh = pnBck.uAccountID > node.uAccountID;
bool bHigh = backNode.account_ > node.account_;
if (!sleBck)
{
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID (pnBck.uAccountID);
WriteLog (lsWARNING, RippleCalc)
<< "pushNode: delay: can't receive IOUs from "
<< "non-existent issuer: " << backNode.account_;
resultCode = terNO_ACCOUNT;
}
else if (sleBck->getFieldU32 (sfFlags) & lsfRequireAuth
&& !(sleRippleState->getFieldU32 (sfFlags) & (bHigh ? lsfHighAuth : lsfLowAuth))
&& sleRippleState->getFieldAmount(sfBalance) == zero) // CHECKME
else if ((sleBck->getFieldU32 (sfFlags) & lsfRequireAuth) &&
!(sleRippleState->getFieldU32 (sfFlags) &
(bHigh ? lsfHighAuth : lsfLowAuth)) &&
sleRippleState->getFieldAmount(sfBalance) == zero)
{
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from issuer without auth.";
WriteLog (lsWARNING, RippleCalc)
<< "pushNode: delay: can't receive IOUs from "
<< "issuer without auth.";
resultCode = terNO_AUTH;
}
if (resultCode == tesSUCCESS)
{
STAmount saOwed = lesEntries.rippleOwed (node.uAccountID, pnBck.uAccountID, node.uCurrencyID);
STAmount saLimit;
STAmount saOwed = lesEntries.rippleOwed (
node.account_, backNode.account_, node.currency_);
STAmount saLimit;
if (saOwed <= zero
&& -saOwed >= (saLimit = lesEntries.rippleLimit (node.uAccountID, pnBck.uAccountID, node.uCurrencyID)))
{
WriteLog (lsWARNING, RippleCalc) <<
"pushNode: dry:" <<
" saOwed=" << saOwed <<
" saLimit=" << saLimit;
if (saOwed <= zero) {
saLimit = lesEntries.rippleLimit (
node.account_, backNode.account_,
node.currency_);
if (-saOwed >= saLimit)
{
WriteLog (lsWARNING, RippleCalc) <<
"pushNode: dry:" <<
" saOwed=" << saOwed <<
" saLimit=" << saLimit;
resultCode = tecPATH_DRY;
resultCode = tecPATH_DRY;
}
}
}
}
@@ -301,34 +307,35 @@ TER PathState::pushNode (
}
if (resultCode == tesSUCCESS)
{
nodes_.push_back (node);
}
}
else
{
// Offer link
// Offers bridge a change in currency & issuer or just a change in issuer.
node.uIssuerID = bIssuer
? uIssuerID
: !!node.uCurrencyID
? !!previousNode.uIssuerID
? previousNode.uIssuerID // Default to previous issuer
: previousNode.uAccountID // Or previous account if no previous issuer.
: ACCOUNT_XRP;
node.saRateMax = saZero;
node.saRevDeliver = STAmount (node.uCurrencyID, node.uIssuerID);
node.saFwdDeliver = node.saRevDeliver;
// Offer link.
//
// Offers bridge a change in currency and issuer, or just a change in
// issuer.
node.issuer_ = hasIssuer
? issuer
: !!node.currency_
? !!previousNode.issuer_
? Account(previousNode.issuer_) // Default to previous issuer
: Account(previousNode.account_)
// Or previous account if no previous issuer.
: XRP_ACCOUNT;
node.saRateMax = saZero;
node.saRevDeliver = STAmount (node.currency_, node.issuer_);
node.saFwdDeliver = node.saRevDeliver;
if (node.uCurrencyID.isZero() != node.uIssuerID.isZero())
if (node.currency_.isZero() != node.issuer_.isZero())
{
WriteLog (lsDEBUG, RippleCalc)
<< "pushNode: currency is inconsistent with issuer.";
resultCode = temBAD_PATH;
}
else if (previousNode.uCurrencyID == node.uCurrencyID &&
previousNode.uIssuerID == node.uIssuerID)
else if (previousNode.currency_ == node.currency_ &&
previousNode.issuer_ == node.issuer_)
{
WriteLog (lsDEBUG, RippleCalc) <<
"pushNode: bad path: offer to same currency and issuer";
@@ -337,10 +344,10 @@ TER PathState::pushNode (
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for offer.";
// Insert intermediary issuer account if needed.
resultCode = pushImply (
ACCOUNT_XRP, // Rippling, but offers don't have an account.
previousNode.uCurrencyID,
previousNode.uIssuerID);
resultCode = pushImpliedNodes (
XRP_ACCOUNT, // Rippling, but offers don't have an account.
previousNode.currency_,
previousNode.issuer_);
}
if (resultCode == tesSUCCESS)
@@ -366,89 +373,112 @@ TER PathState::pushNode (
//
// If you're paying USD and getting bitcoins, there has to be an order book in
// between.
// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, terNO_ACCOUNT, terNO_AUTH, or temBAD_PATH_LOOP
void PathState::setExpanded (
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID
)
// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, terNO_ACCOUNT, terNO_AUTH,
// or temBAD_PATH_LOOP
void PathState::expandPath (
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
Account const& uReceiverID,
Account const& uSenderID)
{
uQuality = 1; // Mark path as active.
uQuality = 1; // Mark path as active.
const uint160 uMaxCurrencyID = saInReq.getCurrency ();
const uint160 uMaxIssuerID = saInReq.getIssuer ();
const Currency uMaxCurrencyID = saInReq.getCurrency ();
const Account uMaxIssuerID = saInReq.getIssuer ();
const uint160 uOutCurrencyID = saOutReq.getCurrency ();
const uint160 uOutIssuerID = saOutReq.getIssuer ();
const uint160 uSenderIssuerID = !!uMaxCurrencyID ? uSenderID : ACCOUNT_XRP; // Sender is always issuer for non-XRP.
const Currency uOutCurrencyID = saOutReq.getCurrency ();
const Account uOutIssuerID = saOutReq.getIssuer ();
const Account uSenderIssuerID
= isXRP(uMaxCurrencyID) ? XRP_ACCOUNT : uSenderID;
// Sender is always issuer for non-XRP.
WriteLog (lsTRACE, RippleCalc) << "setExpanded> " << spSourcePath.getJson (0);
WriteLog (lsTRACE, RippleCalc)
<< "expandPath> " << spSourcePath.getJson (0);
lesEntries = lesSource.duplicate ();
lesEntries = lesSource.duplicate ();
terStatus = tesSUCCESS;
terStatus = tesSUCCESS;
// XRP with issuer is malformed.
if ((!uMaxCurrencyID && !!uMaxIssuerID) || (!uOutCurrencyID && !!uOutIssuerID))
if ((!uMaxCurrencyID && !!uMaxIssuerID)
|| (!uOutCurrencyID && !!uOutIssuerID))
{
terStatus = temBAD_PATH;
}
// Push sending node.
// For non-XRP, issuer is always sending account.
// - Trying to expand, not-compact.
// - Every issuer will be traversed through.
if (tesSUCCESS == terStatus)
if (terStatus == tesSUCCESS)
{
terStatus = pushNode (
!!uMaxCurrencyID
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
uSenderID,
uMaxCurrencyID, // Max specifies the currency.
uSenderIssuerID);
!isXRP(uMaxCurrencyID)
? STPathElement::typeAccount | STPathElement::typeCurrency |
STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
uSenderID,
uMaxCurrencyID, // Max specifies the currency.
uSenderIssuerID);
}
WriteLog (lsDEBUG, RippleCalc) << "setExpanded: pushed:" <<
" account=" << RippleAddress::createHumanAccountID (uSenderID) <<
" currency=" << STAmount::createHumanCurrency (uMaxCurrencyID) <<
" issuer=" << RippleAddress::createHumanAccountID (uSenderIssuerID);
WriteLog (lsDEBUG, RippleCalc)
<< "expandPath: pushed:"
<< " account=" << uSenderID
<< " currency=" << uMaxCurrencyID
<< " issuer=" << uSenderIssuerID;
if (tesSUCCESS == terStatus
&& uMaxIssuerID != uSenderIssuerID) // Issuer was not same as sender.
&& uMaxIssuerID != uSenderIssuerID)
// Issuer was not same as sender.
{
// May have an implied account node.
// - If it was XRP, then issuers would have matched.
// Figure out next node properties for implied node.
const uint160 uNxtCurrencyID = spSourcePath.size ()
? spSourcePath.getElement (0).getCurrency () // Use next node.
: uOutCurrencyID; // Use send.
const uint160 nextAccountID = spSourcePath.size ()
? spSourcePath.getElement (0).getAccountID ()
: !!uOutCurrencyID
? uOutIssuerID == uReceiverID
? uReceiverID
: uOutIssuerID // Use implied node.
: ACCOUNT_XRP;
const auto uNxtCurrencyID = spSourcePath.size ()
? Currency(spSourcePath.getElement (0).getCurrency ())
// Use next node.
: uOutCurrencyID;
// Use send.
WriteLog (lsDEBUG, RippleCalc) << "setExpanded: implied check:" <<
" uMaxIssuerID=" << RippleAddress::createHumanAccountID (uMaxIssuerID) <<
" uSenderIssuerID=" << RippleAddress::createHumanAccountID (uSenderIssuerID) <<
" uNxtCurrencyID=" << STAmount::createHumanCurrency (uNxtCurrencyID) <<
" nextAccountID=" << RippleAddress::createHumanAccountID (nextAccountID);
// TODO(tom): complexify this next logic further in case someone
// understands it.
const auto nextAccountID = spSourcePath.size ()
? Account(spSourcePath.getElement (0).getAccountID ())
: !isXRP(uOutCurrencyID)
? (uOutIssuerID == uReceiverID)
? Account(uReceiverID)
: Account(uOutIssuerID) // Use implied node.
: XRP_ACCOUNT;
// Can't just use push implied, because it can't compensate for next account.
if (!uNxtCurrencyID // Next is XRP, offer next. Must go through issuer.
|| uMaxCurrencyID != uNxtCurrencyID // Next is different currency, offer next...
|| uMaxIssuerID != nextAccountID) // Next is not implied issuer
WriteLog (lsDEBUG, RippleCalc)
<< "expandPath: implied check:"
<< " uMaxIssuerID=" << uMaxIssuerID
<< " uSenderIssuerID=" << uSenderIssuerID
<< " uNxtCurrencyID=" << uNxtCurrencyID
<< " nextAccountID=" << nextAccountID;
// Can't just use push implied, because it can't compensate for next
// account.
if (!uNxtCurrencyID
// Next is XRP, offer next. Must go through issuer.
|| uMaxCurrencyID != uNxtCurrencyID
// Next is different currency, offer next...
|| uMaxIssuerID != nextAccountID)
// Next is not implied issuer
{
WriteLog (lsDEBUG, RippleCalc) << "setExpanded: sender implied:" <<
" account=" << RippleAddress::createHumanAccountID (uMaxIssuerID) <<
" currency=" << STAmount::createHumanCurrency (uMaxCurrencyID) <<
" issuer=" << RippleAddress::createHumanAccountID (uMaxIssuerID);
WriteLog (lsDEBUG, RippleCalc)
<< "expandPath: sender implied:"
<< " account=" << uMaxIssuerID
<< " currency=" << uMaxCurrencyID
<< " issuer=" << uMaxIssuerID;
// Add account implied by SendMax.
terStatus = pushNode (
!!uMaxCurrencyID
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
terStatus = pushNode (
!isXRP(uMaxCurrencyID)
? STPathElement::typeAccount | STPathElement::typeCurrency |
STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
uMaxIssuerID,
uMaxCurrencyID,
@@ -456,95 +486,104 @@ void PathState::setExpanded (
}
}
BOOST_FOREACH (const STPathElement & speElement, spSourcePath)
for (auto & speElement: spSourcePath)
{
if (tesSUCCESS == terStatus)
if (terStatus == tesSUCCESS)
{
WriteLog (lsTRACE, RippleCalc) << "setExpanded: element in path";
terStatus = pushNode (
WriteLog (lsTRACE, RippleCalc) << "expandPath: element in path";
terStatus = pushNode (
speElement.getNodeType (), speElement.getAccountID (),
speElement.getCurrency (), speElement.getIssuerID ());
}
}
auto const& previousNode = nodes_.back ();
auto const& previousNode = nodes_.back ();
if (tesSUCCESS == terStatus
&& !!uOutCurrencyID // Next is not XRP
&& uOutIssuerID != uReceiverID // Out issuer is not receiver
&& (previousNode.uCurrencyID != uOutCurrencyID // Previous will be an offer.
|| previousNode.uAccountID != uOutIssuerID)) // Need the implied issuer.
if (terStatus == tesSUCCESS
&& !isXRP(uOutCurrencyID) // Next is not XRP
&& uOutIssuerID != uReceiverID // Out issuer is not receiver
&& (previousNode.currency_ != uOutCurrencyID
// Previous will be an offer.
|| previousNode.account_ != uOutIssuerID))
// Need the implied issuer.
{
// Add implied account.
WriteLog (lsDEBUG, RippleCalc) << "setExpanded: receiver implied:" <<
" account=" << RippleAddress::createHumanAccountID (uOutIssuerID) <<
" currency=" << STAmount::createHumanCurrency (uOutCurrencyID) <<
" issuer=" << RippleAddress::createHumanAccountID (uOutIssuerID);
WriteLog (lsDEBUG, RippleCalc)
<< "expandPath: receiver implied:"
<< " account=" << uOutIssuerID
<< " currency=" << uOutCurrencyID
<< " issuer=" << uOutIssuerID;
terStatus = pushNode (
!!uOutCurrencyID
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
!isXRP(uOutCurrencyID)
? STPathElement::typeAccount | STPathElement::typeCurrency |
STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
uOutIssuerID,
uOutCurrencyID,
uOutIssuerID);
}
if (tesSUCCESS == terStatus)
if (terStatus == tesSUCCESS)
{
// Create receiver node.
// Last node is always an account.
terStatus = pushNode (
!!uOutCurrencyID
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
!isXRP(uOutCurrencyID)
? STPathElement::typeAccount | STPathElement::typeCurrency |
STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
uReceiverID, // Receive to output
uOutCurrencyID, // Desired currency
uReceiverID);
}
if (tesSUCCESS == terStatus)
if (terStatus == tesSUCCESS)
{
// Look for first mention of source in nodes and detect loops.
// Note: The output is not allowed to be a source.
const unsigned int uNodes = nodes_.size ();
for (unsigned int nodeIndex = 0; tesSUCCESS == terStatus && nodeIndex != uNodes; ++nodeIndex)
for (unsigned int nodeIndex = 0;
tesSUCCESS == terStatus && nodeIndex != uNodes; ++nodeIndex)
{
const auto& node = nodes_[nodeIndex];
AccountCurrencyIssuer aci(
node.uAccountID, node.uCurrencyID, node.uIssuerID);
node.account_, node.currency_, node.issuer_);
if (!umForward.insert (std::make_pair (aci, nodeIndex)).second)
{
// Failed to insert. Have a loop.
WriteLog (lsDEBUG, RippleCalc) <<
"setExpanded: loop detected: " << getJson ();
"expandPath: loop detected: " << getJson ();
terStatus = temBAD_PATH_LOOP;
}
}
}
WriteLog (lsDEBUG, RippleCalc) << "setExpanded:" <<
" in=" << STAmount::createHumanCurrency (uMaxCurrencyID) <<
"/" << RippleAddress::createHumanAccountID (uMaxIssuerID) <<
" out=" << STAmount::createHumanCurrency (uOutCurrencyID) <<
"/" << RippleAddress::createHumanAccountID (uOutIssuerID) <<
": " << getJson ();
WriteLog (lsDEBUG, RippleCalc)
<< "expandPath:"
<< " in=" << uMaxCurrencyID
<< "/" << uMaxIssuerID
<< " out=" << uOutCurrencyID
<< "/" << uOutIssuerID
<< ": " << getJson ();
}
/** 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 [second]->[third]
Disallowed if 'second' set no ripple on [first]->[second] and
[second]->[third]
*/
void PathState::checkNoRipple (
uint160 const& firstAccount,
uint160 const& secondAccount, // This is the account whose constraints we are checking
uint160 const& thirdAccount,
uint160 const& currency)
Account const& firstAccount,
Account const& secondAccount,
// This is the account whose constraints we are checking
Account const& thirdAccount,
Currency const& currency)
{
// fetch the ripple lines into and out of this node
SLE::pointer sleIn = lesEntries.entryCache (ltRIPPLE_STATE,
@@ -562,19 +601,23 @@ void PathState::checkNoRipple (
sleOut->getFieldU32 (sfFlags) &
((secondAccount > thirdAccount) ? lsfHighNoRipple : lsfLowNoRipple))
{
WriteLog (lsINFO, RippleCalc) << "Path violates noRipple constraint between " <<
RippleAddress::createHumanAccountID (firstAccount) << ", " <<
RippleAddress::createHumanAccountID (secondAccount) << " and " <<
RippleAddress::createHumanAccountID (thirdAccount);
WriteLog (lsINFO, RippleCalc)
<< "Path violates noRipple constraint between "
<< firstAccount << ", "
<< secondAccount << " and "
<< thirdAccount;
terStatus = terNO_RIPPLE;
}
}
// Check a fully-expanded path to make sure it doesn't violate no-Ripple settings
void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrcAccountID)
// Check a fully-expanded path to make sure it doesn't violate no-Ripple
// settings.
void PathState::checkNoRipple (
Account const& uDstAccountID, Account const& uSrcAccountID)
{
// There must be at least one node for there to be two consecutive ripple lines
// There must be at least one node for there to be two consecutive ripple
// lines.
if (nodes_.size() == 0)
return;
@@ -583,14 +626,14 @@ void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrc
// There's just one link in the path
// We only need to check source-node-dest
if (nodes_[0].isAccount() &&
(nodes_[0].uAccountID != uSrcAccountID) &&
(nodes_[0].uAccountID != uDstAccountID))
(nodes_[0].account_ != uSrcAccountID) &&
(nodes_[0].account_ != uDstAccountID))
{
if (saInReq.getCurrency() != saOutReq.getCurrency())
terStatus = terNO_LINE;
else
checkNoRipple (uSrcAccountID, nodes_[0].uAccountID, uDstAccountID,
nodes_[0].uCurrencyID);
checkNoRipple (uSrcAccountID, nodes_[0].account_, uDstAccountID,
nodes_[0].currency_);
}
return;
}
@@ -598,17 +641,17 @@ void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrc
// Check source <-> first <-> second
if (nodes_[0].isAccount() &&
nodes_[1].isAccount() &&
(nodes_[0].uAccountID != uSrcAccountID))
(nodes_[0].account_ != uSrcAccountID))
{
if ((nodes_[0].uCurrencyID != nodes_[1].uCurrencyID))
if ((nodes_[0].currency_ != nodes_[1].currency_))
{
terStatus = terNO_LINE;
return;
}
else
{
checkNoRipple (uSrcAccountID, nodes_[0].uAccountID, nodes_[1].uAccountID,
nodes_[0].uCurrencyID);
checkNoRipple (uSrcAccountID, nodes_[0].account_, nodes_[1].account_,
nodes_[0].currency_);
if (tesSUCCESS != terStatus)
return;
}
@@ -618,17 +661,17 @@ void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrc
size_t s = nodes_.size() - 2;
if (nodes_[s].isAccount() &&
nodes_[s + 1].isAccount() &&
(uDstAccountID != nodes_[s+1].uAccountID))
(uDstAccountID != nodes_[s+1].account_))
{
if ((nodes_[s].uCurrencyID != nodes_[s+1].uCurrencyID))
if ((nodes_[s].currency_ != nodes_[s+1].currency_))
{
terStatus = terNO_LINE;
return;
}
else
{
checkNoRipple (nodes_[s].uAccountID, nodes_[s+1].uAccountID, uDstAccountID,
nodes_[s].uCurrencyID);
checkNoRipple (nodes_[s].account_, nodes_[s+1].account_,
uDstAccountID, nodes_[s].currency_);
if (tesSUCCESS != terStatus)
return;
}
@@ -643,16 +686,16 @@ void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrc
nodes_[i + 1].isAccount())
{ // Two consecutive account-to-account links
uint160 const& currencyID = nodes_[i].uCurrencyID;
if ((nodes_[i-1].uCurrencyID != currencyID) ||
(nodes_[i+1].uCurrencyID != currencyID))
uint160 const& currencyID = nodes_[i].currency_;
if ((nodes_[i-1].currency_ != currencyID) ||
(nodes_[i+1].currency_ != currencyID))
{
terStatus = temBAD_PATH;
return;
}
checkNoRipple (
nodes_[i-1].uAccountID, nodes_[i].uAccountID, nodes_[i+1].uAccountID,
currencyID);
nodes_[i-1].account_, nodes_[i].account_, nodes_[i+1].account_,
currencyID);
if (terStatus != tesSUCCESS)
return;
}
@@ -660,7 +703,8 @@ void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrc
}
}
// This is for debugging not end users. Output names can be changed without warning.
// This is for debugging not end users. Output names can be changed without
// warning.
Json::Value PathState::getJson () const
{
Json::Value jvPathState (Json::objectValue);

View File

@@ -32,6 +32,9 @@ class PathState : public CountedObject <PathState>
typedef std::vector<uint256> OfferIndexList;
typedef std::vector<std::shared_ptr<PathState>> List;
typedef path::Account Account;
typedef path::Currency Currency;
PathState (const STAmount& saSend, const STAmount& saSendMax)
: saInReq (saSendMax)
, saOutReq (saSend)
@@ -48,11 +51,11 @@ class PathState : public CountedObject <PathState>
void clear();
void setExpanded (
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID
void expandPath (
LedgerEntrySet const& lesSource,
STPath const& spSourcePath,
Account const& uReceiverID,
Account const& uSenderID
);
path::Node::List& nodes() { return nodes_; }
@@ -84,14 +87,15 @@ class PathState : public CountedObject <PathState>
void setIndex (int i) { mIndex = i; }
int index() const { return mIndex; }
void checkNoRipple (uint160 const& destinationAccountID,
uint160 const& sourceAccountID);
void checkNoRipple (Account const& destinationAccountID,
Account const& sourceAccountID);
static bool lessPriority (PathState& lhs, PathState& rhs);
LedgerEntrySet& ledgerEntries() { return lesEntries; }
private:
void checkNoRipple (uint160 const&, uint160 const&, uint160 const&, uint160 const&);
void checkNoRipple (
Account const&, Account const&, Account const&, Currency const&);
TER terStatus;
path::Node::List nodes_;
@@ -110,25 +114,28 @@ class PathState : public CountedObject <PathState>
LedgerEntrySet lesEntries;
int mIndex; // Index/rank amoung siblings.
std::uint64_t uQuality; // 0 = no quality/liquity left.
const STAmount& saInReq; // --> Max amount to spend by sender.
STAmount saInAct; // --> Amount spent by sender so far.
STAmount saInPass; // <-- Amount spent by sender.
const STAmount& saOutReq; // --> Amount to send.
STAmount saOutAct; // --> Amount actually sent so far.
STAmount saOutPass; // <-- Amount actually sent.
int mIndex; // Index/rank amoung siblings.
std::uint64_t uQuality; // 0 = no quality/liquity left.
const STAmount& saInReq; // --> Max amount to spend by sender.
STAmount saInAct; // --> Amount spent by sender so far.
STAmount saInPass; // <-- Amount spent by sender.
const STAmount& saOutReq; // --> Amount to send.
STAmount saOutAct; // --> Amount actually sent so far.
STAmount saOutPass; // <-- Amount actually sent.
// If true, all liquidity on this path has been consumed.
bool allLiquidityConsumed_;
TER pushNode (
const int iType, const uint160& uAccountID, const uint160& uCurrencyID,
const uint160& uIssuerID);
int const iType,
Account const& account,
Currency const& currency,
Account const& issuer);
TER pushImply (
const uint160& uAccountID, const uint160& uCurrencyID,
const uint160& uIssuerID);
TER pushImpliedNodes (
Account const& account,
Currency const& currency,
Account const& issuer);
};
} // ripple

View File

@@ -405,7 +405,7 @@ boost::unordered_set<uint160> usAccountSourceCurrencies (
// YYY Only bother if they are above reserve
if (includeXRP)
usCurrencies.insert (uint160 (CURRENCY_XRP));
usCurrencies.insert (uint160 (XRP_CURRENCY));
// List of ripple lines.
AccountItems& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ()));
@@ -436,7 +436,7 @@ boost::unordered_set<uint160> usAccountDestCurrencies (
boost::unordered_set<uint160> usCurrencies;
if (includeXRP)
usCurrencies.insert (uint160 (CURRENCY_XRP)); // Even if account doesn't exist
usCurrencies.insert (uint160 (XRP_CURRENCY)); // Even if account doesn't exist
// List of ripple lines.
AccountItems& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ()));
@@ -744,7 +744,10 @@ void Pathfinder::addLink(
{ // to XRP only
if (!bOnXRP && getApp().getOrderBookDB().isBookToXRP(uEndIssuer, uEndCurrency))
{
incompletePaths.assembleAdd(currentPath, STPathElement(STPathElement::typeCurrency, ACCOUNT_XRP, CURRENCY_XRP, ACCOUNT_XRP));
STPathElement pathElement(
STPathElement::typeCurrency,
XRP_ACCOUNT, XRP_CURRENCY, XRP_ACCOUNT);
incompletePaths.assembleAdd(currentPath, pathElement);
}
}
else
@@ -755,9 +758,15 @@ void Pathfinder::addLink(
WriteLog (lsTRACE, Pathfinder) << books.size() << " books found from this currency/issuer";
BOOST_FOREACH(OrderBook::ref book, books)
{
if (!currentPath.hasSeen (ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut()) &&
!matchesOrigin(book->getCurrencyOut(), book->getIssuerOut()) &&
(!bDestOnly || (book->getCurrencyOut() == mDstAmount.getCurrency())))
if (!currentPath.hasSeen (
XRP_ACCOUNT,
book->getCurrencyOut(),
book->getIssuerOut()) &&
!matchesOrigin(
book->getCurrencyOut(),
book->getIssuerOut()) &&
(!bDestOnly ||
(book->getCurrencyOut() == mDstAmount.getCurrency())))
{
STPath newPath(currentPath);
@@ -765,7 +774,9 @@ void Pathfinder::addLink(
{ // to XRP
// add the order book itself
newPath.addElement(STPathElement(STPathElement::typeCurrency, ACCOUNT_XRP, CURRENCY_XRP, ACCOUNT_XRP));
newPath.addElement(STPathElement(
STPathElement::typeCurrency,
XRP_ACCOUNT, XRP_CURRENCY, XRP_ACCOUNT));
if (mDstAmount.getCurrency().isZero())
{ // destination is XRP, add account and path is complete
@@ -779,7 +790,7 @@ void Pathfinder::addLink(
{ // Don't want the book if we've already seen the issuer
// add the order book itself
newPath.addElement(STPathElement(STPathElement::typeCurrency | STPathElement::typeIssuer,
ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut()));
XRP_ACCOUNT, book->getCurrencyOut(), book->getIssuerOut()));
if ((book->getIssuerOut() == mDstAccountID) && book->getCurrencyOut() == mDstAmount.getCurrency())
{ // with the destination account, this path is complete

View File

@@ -19,16 +19,17 @@
#include <ripple/module/app/paths/Calculators.h>
#include <ripple/module/app/paths/CalcNode.cpp>
#include <ripple/module/app/paths/CalcNodeAccountFwd.cpp>
#include <ripple/module/app/paths/CalcNodeAccountRev.cpp>
#include <ripple/module/app/paths/CalcNodeAdvance.cpp>
#include <ripple/module/app/paths/CalcNodeDeliverFwd.cpp>
#include <ripple/module/app/paths/CalcNodeDeliverRev.cpp>
#include <ripple/module/app/paths/CalcNodeOffer.cpp>
#include <ripple/module/app/paths/CalcNodeRipple.cpp>
#include <ripple/module/app/paths/ComputeRippleLiquidity.cpp>
#include <ripple/module/app/paths/ComputeAccountLiquidityForward.cpp>
#include <ripple/module/app/paths/ComputeAccountLiquidityReverse.cpp>
#include <ripple/module/app/paths/ComputeLiquidity.cpp>
#include <ripple/module/app/paths/ComputeOfferLiquidity.cpp>
#include <ripple/module/app/paths/Node.cpp>
#include <ripple/module/app/paths/PathNext.cpp>
#include <ripple/module/app/paths/Types.cpp>
namespace ripple {
@@ -57,13 +58,13 @@ TER rippleCalculate (
PathState::List& pathStateList,
// Issuer:
// XRP: ACCOUNT_XRP
// XRP: XRP_ACCOUNT
// non-XRP: uSrcAccountID (for any issuer) or another account with trust
// node.
const STAmount& saMaxAmountReq, // --> -1 = no limit.
// Issuer:
// XRP: ACCOUNT_XRP
// XRP: XRP_ACCOUNT
// non-XRP: uDstAccountID (for any issuer) or another account with trust
// node.
const STAmount& saDstAmountReq,
@@ -119,7 +120,7 @@ TER rippleCalculate (
if (!pspDirect)
return temUNKNOWN;
pspDirect->setExpanded (
pspDirect->expandPath (
activeLedger, STPath (), uDstAccountID, uSrcAccountID);
if (tesSUCCESS == pspDirect->status())
@@ -167,7 +168,7 @@ TER rippleCalculate (
<< " uSrcAccountID:"
<< RippleAddress::createHumanAccountID (uSrcAccountID);
pspExpanded->setExpanded (
pspExpanded->expandPath (
activeLedger, spPath, uDstAccountID, uSrcAccountID);
if (tesSUCCESS == pspExpanded->status())
@@ -200,8 +201,8 @@ TER rippleCalculate (
else
resultCode = temUNCERTAIN;
saMaxAmountAct = zeroed(saMaxAmountReq);
saDstAmountAct = zeroed(saDstAmountReq);
saMaxAmountAct = saMaxAmountReq.zeroed();
saDstAmountAct = saDstAmountReq.zeroed();
// When processing, we don't want to complicate directory walking with
// deletion.
@@ -426,25 +427,25 @@ TER rippleCalculate (
if (resultCode == tesSUCCESS)
{
// Delete became unfunded offers.
for (auto const& uOfferIndex: vuUnfundedBecame)
for (auto const& offerIndex: vuUnfundedBecame)
{
if (resultCode == tesSUCCESS)
{
WriteLog (lsDEBUG, RippleCalc)
<< "Became unfunded " << to_string (uOfferIndex);
resultCode = activeLedger.offerDelete (uOfferIndex);
<< "Became unfunded " << to_string (offerIndex);
resultCode = activeLedger.offerDelete (offerIndex);
}
}
}
// Delete found unfunded offers.
for (auto const& uOfferIndex: rc.mUnfundedOffers)
for (auto const& offerIndex: rc.mUnfundedOffers)
{
if (resultCode == tesSUCCESS)
{
WriteLog (lsDEBUG, RippleCalc)
<< "Delete unfunded " << to_string (uOfferIndex);
resultCode = activeLedger.offerDelete (uOfferIndex);
<< "Delete unfunded " << to_string (offerIndex);
resultCode = activeLedger.offerDelete (offerIndex);
}
}
}

View File

@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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/module/app/paths/Types.h>
namespace ripple {
const path::Account XRP_ACCOUNT(0);
const path::Currency XRP_CURRENCY(0);
} // ripple

View File

@@ -25,18 +25,19 @@ namespace ripple {
// account id, currency id, issuer id.
typedef std::tuple <uint160, uint160, uint160> AccountCurrencyIssuer;
std::size_t hash_value (const AccountCurrencyIssuer& asValue);
// Map of account, currency, issuer to node index.
typedef ripple::unordered_map <AccountCurrencyIssuer, unsigned int>
AccountCurrencyIssuerToNodeIndex;
/** Returns a copy of an STAmount with the same currency and issuer but the
amount set to zero. */
inline STAmount zeroed(const STAmount& a)
{
return STAmount(a.getCurrency(), a.getIssuer());
}
namespace path {
typedef core::Account Account;
typedef core::Currency Currency;
} // path
extern const path::Account XRP_ACCOUNT;
extern const path::Currency XRP_CURRENCY;
} // ripple

View File

@@ -26,7 +26,7 @@ TER CancelOffer::doApply ()
std::uint32_t const uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence);
m_journal.debug <<
"uAccountSequenceNext=" << uAccountSequenceNext <<
"uAccountSequenceNext=" << uAccountSequenceNext <<
" uOfferSequence=" << uOfferSequence;
std::uint32_t const uTxFlags (mTxn.getFlags ());
@@ -41,16 +41,16 @@ TER CancelOffer::doApply ()
if (!uOfferSequence || uAccountSequenceNext - 1 <= uOfferSequence)
{
m_journal.trace <<
"uAccountSequenceNext=" << uAccountSequenceNext <<
"uAccountSequenceNext=" << uAccountSequenceNext <<
" uOfferSequence=" << uOfferSequence;
return temBAD_SEQUENCE;
}
uint256 const uOfferIndex (
uint256 const offerIndex (
Ledger::getOfferIndex (mTxnAccountID, uOfferSequence));
SLE::pointer sleOffer (
mEngine->entryCache (ltOFFER, uOfferIndex));
mEngine->entryCache (ltOFFER, offerIndex));
if (sleOffer)
{
@@ -62,9 +62,9 @@ TER CancelOffer::doApply ()
m_journal.warning <<
"OfferCancel: offer not found: " <<
RippleAddress::createHumanAccountID (mTxnAccountID) <<
RippleAddress::createHumanAccountID (mTxnAccountID) <<
" : " << uOfferSequence <<
" : " << to_string (uOfferIndex);
" : " << to_string (offerIndex);
return tesSUCCESS;
}

View File

@@ -280,38 +280,49 @@ bool CreateOfferLegacy::applyOffer (
{
// Compute fees in a rounding safe way.
STAmount saTransferRate = STAmount (CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9);
m_journal.info << "applyOffer: saTransferRate=" << saTransferRate.getFullText ();
STAmount transferRate (CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9);
m_journal.info << "applyOffer: transferRate="
<< transferRate.getFullText ();
// TakerCost includes transfer fees.
STAmount saTakerCost = STAmount::mulRound (saTakerPaid, saTransferRate, true);
STAmount saTakerCost = STAmount::mulRound (
saTakerPaid, transferRate, true);
m_journal.info << "applyOffer: saTakerCost=" << saTakerCost.getFullText ();
m_journal.info << "applyOffer: saTakerFunds=" << saTakerFunds.getFullText ();
m_journal.info << "applyOffer: saTakerCost="
<< saTakerCost.getFullText ();
m_journal.info << "applyOffer: saTakerFunds="
<< saTakerFunds.getFullText ();
saTakerIssuerFee = saTakerCost > saTakerFunds
? saTakerFunds - saTakerPaid // Not enough funds to cover fee, stiff issuer the rounding error.
? saTakerFunds - saTakerPaid
// Not enough funds to cover fee, stiff issuer the rounding error.
: saTakerCost - saTakerPaid;
m_journal.info << "applyOffer: saTakerIssuerFee=" << saTakerIssuerFee.getFullText ();
m_journal.info << "applyOffer: saTakerIssuerFee="
<< saTakerIssuerFee.getFullText ();
assert (saTakerIssuerFee >= zero);
}
if (uOfferPaysRate == QUALITY_ONE)
{
saOfferIssuerFee = STAmount (saTakerGot.getCurrency (), saTakerGot.getIssuer ());
saOfferIssuerFee = STAmount (
saTakerGot.getCurrency (), saTakerGot.getIssuer ());
}
else
{
// Compute fees in a rounding safe way.
STAmount saOfferCost = STAmount::mulRound (saTakerGot, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9), true);
STAmount saOfferCost = STAmount::mulRound (
saTakerGot, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9),
true);
saOfferIssuerFee = saOfferCost > saOfferFunds
? saOfferFunds - saTakerGot // Not enough funds to cover fee, stiff issuer the rounding error.
: saOfferCost - saTakerGot;
saOfferIssuerFee = saOfferCost > saOfferFunds
? saOfferFunds - saTakerGot
// Not enough funds to cover fee, stiff issuer the rounding error.
: saOfferCost - saTakerGot;
}
m_journal.info << "applyOffer: saTakerGot=" << saTakerGot.getFullText ();
return saTakerGot >= saOfferPaysAvailable; // True, if consumed offer.
return saTakerGot >= saOfferPaysAvailable;
// True, if consumed offer.
}
/** Take as much as possible.
@@ -366,8 +377,10 @@ TER CreateOfferLegacy::takeOffers (
// Accounts touched.
std::unordered_set<uint160, beast::hardened_hash<uint160>> usAccountTouched;
saTakerPaid = STAmount (saTakerPays.getCurrency (), saTakerPays.getIssuer ());
saTakerGot = STAmount (saTakerGets.getCurrency (), saTakerGets.getIssuer ());
saTakerPaid = STAmount (
saTakerPays.getCurrency (), saTakerPays.getIssuer ());
saTakerGot = STAmount (
saTakerGets.getCurrency (), saTakerGets.getIssuer ());
bUnfunded = false;
// TODO: need to track the synthesized book (source->XRP + XRP->target)
@@ -654,14 +667,14 @@ TER CreateOfferLegacy::takeOffers (
if (tesSUCCESS == terResult)
{
// On success, delete offers that became unfunded.
for (auto uOfferIndex : usOfferUnfundedBecame)
for (auto offerIndex : usOfferUnfundedBecame)
{
m_journal.debug <<
"takeOffers: became unfunded: " <<
to_string (uOfferIndex);
to_string (offerIndex);
lesActive.offerDelete (uOfferIndex);
lesActive.offerDelete (offerIndex);
}
}
@@ -1116,11 +1129,11 @@ TER CreateOfferLegacy::doApply ()
{
// Go through the list of unfunded offers and remove them
for (auto const& uOfferIndex : usOfferUnfundedFound)
for (auto const& offerIndex : usOfferUnfundedFound)
{
m_journal.trace <<
"takeOffers: found unfunded: " << to_string (uOfferIndex);
lesActive.offerDelete (uOfferIndex);
"takeOffers: found unfunded: " << to_string (offerIndex);
lesActive.offerDelete (offerIndex);
}
// Go through the list of offers not found and remove them from the

View File

@@ -27,7 +27,7 @@ TER SetTrust::doApply ()
bool const bQualityIn (mTxn.isFieldPresent (sfQualityIn));
bool const bQualityOut (mTxn.isFieldPresent (sfQualityOut));
uint160 const uCurrencyID (saLimitAmount.getCurrency ());
uint160 const currency (saLimitAmount.getCurrency ());
uint160 uDstAccountID (saLimitAmount.getIssuer ());
// true, iff current is high account.
@@ -94,7 +94,7 @@ TER SetTrust::doApply ()
SLE::pointer selDelete (
mEngine->entryCache (ltRIPPLE_STATE,
Ledger::getRippleStateIndex (
mTxnAccountID, uDstAccountID, uCurrencyID)));
mTxnAccountID, uDstAccountID, currency)));
if (selDelete)
{
@@ -133,7 +133,7 @@ TER SetTrust::doApply ()
saLimitAllow.setIssuer (mTxnAccountID);
SLE::pointer sleRippleState (mEngine->entryCache (ltRIPPLE_STATE,
Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, uCurrencyID)));
Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, currency)));
if (sleRippleState)
{
@@ -310,7 +310,7 @@ TER SetTrust::doApply ()
if (uFlagsIn != uFlagsOut)
sleRippleState->setFieldU32 (sfFlags, uFlagsOut);
if (bDefault || CURRENCY_BAD == uCurrencyID)
if (bDefault || CURRENCY_BAD == currency)
{
// Delete.
@@ -350,17 +350,17 @@ TER SetTrust::doApply ()
// Another transaction could create the account and then this transaction would succeed.
terResult = tecNO_LINE_INSUF_RESERVE;
}
else if (CURRENCY_BAD == uCurrencyID)
else if (CURRENCY_BAD == currency)
{
terResult = temBAD_CURRENCY;
}
else
{
// Zero balance in currency.
STAmount saBalance (STAmount (uCurrencyID, ACCOUNT_ONE));
STAmount saBalance (STAmount (currency, ACCOUNT_ONE));
uint256 index (Ledger::getRippleStateIndex (
mTxnAccountID, uDstAccountID, uCurrencyID));
mTxnAccountID, uDstAccountID, currency));
m_journal.trace <<
"doTrustSet: Creating ripple line: " <<

View File

@@ -533,12 +533,12 @@ STAmount* STAmount::construct (SerializerIterator& sit, SField::ref name)
return new STAmount (name, value, true); // negative
}
uint160 uCurrencyID = sit.get160 ();
uint160 currency = sit.get160 ();
if (!uCurrencyID)
if (!currency)
throw std::runtime_error ("invalid non-native currency");
uint160 uIssuerID = sit.get160 ();
uint160 issuer = sit.get160 ();
int offset = static_cast<int> (value >> (64 - 10)); // 10 bits for the offset, sign and "not native" flag
value &= ~ (1023ull << (64 - 10));
@@ -551,13 +551,13 @@ STAmount* STAmount::construct (SerializerIterator& sit, SField::ref name)
if ((value < cMinValue) || (value > cMaxValue) || (offset < cMinOffset) || (offset > cMaxOffset))
throw std::runtime_error ("invalid currency value");
return new STAmount (name, uCurrencyID, uIssuerID, value, offset, isNegative);
return new STAmount (name, currency, issuer, value, offset, isNegative);
}
if (offset != 512)
throw std::runtime_error ("invalid currency value");
return new STAmount (name, uCurrencyID, uIssuerID);
return new STAmount (name, currency, issuer);
}
std::int64_t STAmount::getSNValue () const
@@ -921,13 +921,15 @@ STAmount operator- (const STAmount& v1, const STAmount& v2)
}
// NIKB TODO Make Amount::divide skip math if den == QUALITY_ONE
STAmount STAmount::divide (const STAmount& num, const STAmount& den, const uint160& uCurrencyID, const uint160& uIssuerID)
STAmount STAmount::divide (
const STAmount& num, const STAmount& den, const uint160& currency,
const uint160& issuer)
{
if (den == zero)
throw std::runtime_error ("division by zero");
if (num == zero)
return STAmount (uCurrencyID, uIssuerID);
return STAmount (currency, issuer);
std::uint64_t numVal = num.mValue, denVal = den.mValue;
int numOffset = num.mOffset, denOffset = den.mOffset;
@@ -960,16 +962,18 @@ STAmount STAmount::divide (const STAmount& num, const STAmount& den, const uint1
// 10^16 <= quotient <= 10^18
assert (BN_num_bytes (&v) <= 64);
return STAmount (uCurrencyID, uIssuerID, v.getuint64 () + 5,
return STAmount (currency, issuer, v.getuint64 () + 5,
numOffset - denOffset - 17, num.mIsNegative != den.mIsNegative);
}
STAmount STAmount::multiply (const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID)
STAmount STAmount::multiply (
const STAmount& v1, const STAmount& v2, const uint160& currency,
const uint160& issuer)
{
if (v1 == zero || v2 == zero)
return STAmount (uCurrencyID, uIssuerID);
return STAmount (currency, issuer);
if (v1.mIsNative && v2.mIsNative && uCurrencyID.isZero ())
if (v1.mIsNative && v2.mIsNative && currency.isZero ())
{
std::uint64_t minV = (v1.getSNValue () < v2.getSNValue ()) ? v1.getSNValue () : v2.getSNValue ();
std::uint64_t maxV = (v1.getSNValue () < v2.getSNValue ()) ? v2.getSNValue () : v1.getSNValue ();
@@ -1018,7 +1022,7 @@ STAmount STAmount::multiply (const STAmount& v1, const STAmount& v2, const uint1
// 10^16 <= product <= 10^18
assert (BN_num_bytes (&v) <= 64);
return STAmount (uCurrencyID, uIssuerID, v.getuint64 () + 7, offset1 + offset2 + 14,
return STAmount (currency, issuer, v.getuint64 () + 7, offset1 + offset2 + 14,
v1.mIsNegative != v2.mIsNegative);
}

View File

@@ -200,13 +200,14 @@ STAmount STAmount::subRound (const STAmount& v1, const STAmount& v2, bool roundU
}
}
STAmount STAmount::mulRound (const STAmount& v1, const STAmount& v2,
const uint160& uCurrencyID, const uint160& uIssuerID, bool roundUp)
STAmount STAmount::mulRound (
const STAmount& v1, const STAmount& v2, const uint160& currency,
const uint160& issuer, bool roundUp)
{
if (v1 == zero || v2 == zero)
return STAmount (uCurrencyID, uIssuerID);
return STAmount (currency, issuer);
if (v1.mIsNative && v2.mIsNative && uCurrencyID.isZero ())
if (v1.mIsNative && v2.mIsNative && currency.isZero ())
{
std::uint64_t minV = (v1.getSNValue () < v2.getSNValue ()) ? v1.getSNValue () : v2.getSNValue ();
std::uint64_t maxV = (v1.getSNValue () < v2.getSNValue ()) ? v2.getSNValue () : v1.getSNValue ();
@@ -260,18 +261,19 @@ STAmount STAmount::mulRound (const STAmount& v1, const STAmount& v2,
std::uint64_t amount = v.getuint64 ();
int offset = offset1 + offset2 + 14;
canonicalizeRound (uCurrencyID.isZero (), amount, offset, resultNegative != roundUp);
return STAmount (uCurrencyID, uIssuerID, amount, offset, resultNegative);
canonicalizeRound (currency.isZero (), amount, offset, resultNegative != roundUp);
return STAmount (currency, issuer, amount, offset, resultNegative);
}
STAmount STAmount::divRound (const STAmount& num, const STAmount& den,
const uint160& uCurrencyID, const uint160& uIssuerID, bool roundUp)
STAmount STAmount::divRound (
const STAmount& num, const STAmount& den,
const uint160& currency, const uint160& issuer, bool roundUp)
{
if (den == zero)
throw std::runtime_error ("division by zero");
if (num == zero)
return STAmount (uCurrencyID, uIssuerID);
return STAmount (currency, issuer);
std::uint64_t numVal = num.mValue, denVal = den.mValue;
int numOffset = num.mOffset, denOffset = den.mOffset;
@@ -309,8 +311,8 @@ STAmount STAmount::divRound (const STAmount& num, const STAmount& den,
std::uint64_t amount = v.getuint64 ();
int offset = numOffset - denOffset - 17;
canonicalizeRound (uCurrencyID.isZero (), amount, offset, resultNegative != roundUp);
return STAmount (uCurrencyID, uIssuerID, amount, offset, resultNegative);
canonicalizeRound (currency.isZero (), amount, offset, resultNegative != roundUp);
return STAmount (currency, issuer, amount, offset, resultNegative);
}
} // ripple

View File

@@ -427,7 +427,7 @@ STPathSet* STPathSet::construct (SerializerIterator& s, SField::ref name)
{
int iType = s.get8 ();
if (iType == STPathElement::typeEnd || iType == STPathElement::typeBoundary)
if (iType == STPathElement::typeNone || iType == STPathElement::typeBoundary)
{
if (path.empty ())
{
@@ -439,12 +439,12 @@ STPathSet* STPathSet::construct (SerializerIterator& s, SField::ref name)
paths.push_back (path);
path.clear ();
if (iType == STPathElement::typeEnd)
if (iType == STPathElement::typeNone)
{
return new STPathSet (name, paths);
}
}
else if (iType & ~STPathElement::typeValidBits)
else if (iType & ~STPathElement::typeAll)
{
WriteLog (lsINFO, SerializedType) << "STPathSet: Bad path element: " << iType;
@@ -456,20 +456,20 @@ STPathSet* STPathSet::construct (SerializerIterator& s, SField::ref name)
const bool bCurrency = !! (iType & STPathElement::typeCurrency);
const bool bIssuer = !! (iType & STPathElement::typeIssuer);
uint160 uAccountID;
uint160 account;
uint160 uCurrency;
uint160 uIssuerID;
uint160 issuer;
if (bAccount)
uAccountID = s.get160 ();
account = s.get160 ();
if (bCurrency)
uCurrency = s.get160 ();
if (bIssuer)
uIssuerID = s.get160 ();
issuer = s.get160 ();
path.push_back (STPathElement (uAccountID, uCurrency, uIssuerID, bCurrency));
path.push_back (STPathElement (account, uCurrency, issuer, bCurrency));
}
}
while (1);
@@ -481,15 +481,17 @@ bool STPathSet::isEquivalent (const SerializedType& t) const
return v && (value == v->value);
}
bool STPath::hasSeen (const uint160& uAccountId, const uint160& uCurrencyID, const uint160& uIssuerID) const
bool STPath::hasSeen (
const uint160& uAccountId, const uint160& currency,
const uint160& issuer) const
{
for (int i = 0; i < mPath.size (); ++i)
{
const STPathElement& ele = getElement (i);
if (ele.getAccountID () == uAccountId
&& ele.getCurrency () == uCurrencyID
&& ele.getIssuerID () == uIssuerID)
&& ele.getCurrency () == currency
&& ele.getIssuerID () == issuer)
return true;
}
@@ -619,7 +621,7 @@ void STPathSet::add (Serializer& s) const
bFirst = false;
}
s.add8 (STPathElement::typeEnd);
s.add8 (STPathElement::typeNone);
}
} // ripple

View File

@@ -495,16 +495,16 @@ public:
set (v);
}
STAmount (const uint160& uCurrencyID, const uint160& uIssuerID,
STAmount (const uint160& currency, const uint160& issuer,
std::uint64_t uV = 0, int iOff = 0, bool bNegative = false)
: mCurrency (uCurrencyID), mIssuer (uIssuerID), mValue (uV), mOffset (iOff), mIsNegative (bNegative)
: mCurrency (currency), mIssuer (issuer), mValue (uV), mOffset (iOff), mIsNegative (bNegative)
{
canonicalize ();
}
STAmount (const uint160& uCurrencyID, const uint160& uIssuerID,
STAmount (const uint160& currency, const uint160& issuer,
std::uint32_t uV, int iOff = 0, bool bNegative = false)
: mCurrency (uCurrencyID), mIssuer (uIssuerID), mValue (uV), mOffset (iOff), mIsNegative (bNegative)
: mCurrency (currency), mIssuer (issuer), mValue (uV), mOffset (iOff), mIsNegative (bNegative)
{
canonicalize ();
}
@@ -516,8 +516,8 @@ public:
canonicalize ();
}
STAmount (const uint160& uCurrencyID, const uint160& uIssuerID, std::int64_t v, int iOff = 0)
: mCurrency (uCurrencyID), mIssuer (uIssuerID), mOffset (iOff)
STAmount (const uint160& currency, const uint160& issuer, std::int64_t v, int iOff = 0)
: mCurrency (currency), mIssuer (issuer), mOffset (iOff)
{
set (v);
canonicalize ();
@@ -530,8 +530,8 @@ public:
canonicalize ();
}
STAmount (const uint160& uCurrencyID, const uint160& uIssuerID, int v, int iOff = 0)
: mCurrency (uCurrencyID), mIssuer (uIssuerID), mOffset (iOff)
STAmount (const uint160& currency, const uint160& issuer, int v, int iOff = 0)
: mCurrency (currency), mIssuer (issuer), mOffset (iOff)
{
set (v);
canonicalize ();
@@ -621,6 +621,15 @@ public:
mIsNegative = !mIsNegative;
}
// Return a copy of amount with the same Issuer and Currency but zero value.
STAmount zeroed() const
{
STAmount c(mCurrency, mIssuer);
c = zero;
// See https://ripplelabs.atlassian.net/browse/WC-1847?jql=
return c;
}
void clear ()
{
// VFALCO: Why -100?
@@ -637,11 +646,11 @@ public:
mIsNative = saTmpl.mIsNative;
clear ();
}
void clear (const uint160& uCurrencyID, const uint160& uIssuerID)
void clear (const uint160& currency, const uint160& issuer)
{
mCurrency = uCurrencyID;
mIssuer = uIssuerID;
mIsNative = !uCurrencyID;
mCurrency = currency;
mIssuer = issuer;
mIsNative = !currency;
clear ();
}
@@ -706,8 +715,12 @@ public:
friend STAmount operator+ (const STAmount& v1, const STAmount& v2);
friend STAmount operator- (const STAmount& v1, const STAmount& v2);
static STAmount divide (const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID);
static STAmount divide (const STAmount& v1, const STAmount& v2, const STAmount& saUnit)
static STAmount divide (
const STAmount& v1, const STAmount& v2,
const uint160& currency, const uint160& issuer);
static STAmount divide (
const STAmount& v1, const STAmount& v2, const STAmount& saUnit)
{
return divide (v1, v2, saUnit.getCurrency (), saUnit.getIssuer ());
}
@@ -716,8 +729,12 @@ public:
return divide (v1, v2, v1);
}
static STAmount multiply (const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID);
static STAmount multiply (const STAmount& v1, const STAmount& v2, const STAmount& saUnit)
static STAmount multiply (
const STAmount& v1, const STAmount& v2,
const uint160& currency, const uint160& issuer);
static STAmount multiply (
const STAmount& v1, const STAmount& v2, const STAmount& saUnit)
{
return multiply (v1, v2, saUnit.getCurrency (), saUnit.getIssuer ());
}
@@ -1193,33 +1210,35 @@ private:
friend class Pathfinder;
public:
enum
enum Type
{
typeEnd = 0x00,
typeNone = 0x00,
typeAccount = 0x01, // Rippling through an account (vs taking an offer).
typeCurrency = 0x10, // Currency follows.
typeIssuer = 0x20, // Issuer follows.
typeBoundary = 0xFF, // Boundary between alternate paths.
typeValidBits = (
typeAccount
| typeCurrency
| typeIssuer
), // Bits that may be non-zero.
typeAll = typeAccount | typeCurrency | typeIssuer,
// Combination of all types.
};
public:
STPathElement (const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID,
bool forceCurrency = false)
: mAccountID (uAccountID), mCurrencyID (uCurrencyID), mIssuerID (uIssuerID)
STPathElement (
const uint160& account, const uint160& currency,
const uint160& issuer, bool forceCurrency = false)
: mAccountID (account), mCurrencyID (currency), mIssuerID (issuer)
{
mType =
(uAccountID.isZero () ? 0 : STPathElement::typeAccount)
| ((uCurrencyID.isZero () && !forceCurrency) ? 0 : STPathElement::typeCurrency)
| (uIssuerID.isZero () ? 0 : STPathElement::typeIssuer);
(account.isZero () ? 0 : STPathElement::typeAccount)
| ((currency.isZero () && !forceCurrency) ? 0 :
STPathElement::typeCurrency)
| (issuer.isZero () ? 0 : STPathElement::typeIssuer);
}
STPathElement (unsigned int uType, const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
: mType (uType), mAccountID (uAccountID), mCurrencyID (uCurrencyID), mIssuerID (uIssuerID)
STPathElement (
unsigned int uType, const uint160& account, const uint160& currency,
const uint160& issuer)
: mType (uType), mAccountID (account), mCurrencyID (currency),
mIssuerID (issuer)
{
;
}
@@ -1259,8 +1278,10 @@ public:
bool operator== (const STPathElement& t) const
{
return ((mType & typeAccount) == (t.mType & typeAccount)) &&
(mAccountID == t.mAccountID) && (mCurrencyID == t.mCurrencyID) && (mIssuerID == t.mIssuerID);
return ((mType & typeAccount) == (t.mType & typeAccount))
&& (mAccountID == t.mAccountID)
&& (mCurrencyID == t.mCurrencyID)
&& (mIssuerID == t.mIssuerID);
}
private:
@@ -1313,13 +1334,10 @@ public:
{
mPath.clear ();
}
bool hasSeen (const uint160& uAccountId, const uint160& uCurrencyID, const uint160& uIssuerID) const;
// std::string getText() const;
bool hasSeen (const uint160& account, const uint160& currency,
const uint160& issuer) const;
Json::Value getJson (int) const;
// uint160 mCurrencyID;
// uint160 mCurrentAccount; // what account is at the end of the path
std::vector<STPathElement>::iterator begin ()
{
return mPath.begin ();

View File

@@ -28,7 +28,7 @@
#include <ripple/types/api/Blob.h>
#include <ripple/types/api/strHex.h>
#include <ripple/types/api/ByteOrder.h>
#include <beast/container/hardened_hash.h>
#include <beast/utility/Zero.h>
@@ -150,7 +150,18 @@ public:
SetHex (str);
}
base_uint (base_uint const& other) = default;
base_uint (base_uint<Bits, Tag> const& other) = default;
template <
class OtherTag,
class = std::enable_if_t <
std::is_same <OtherTag, void>::value !=
std::is_same <Tag, void>::value>
>
base_uint (base_uint<Bits, OtherTag> const& other)
{
memcpy (&pn [0], other.data(), Bits / 8);
}
/* Construct from a raw pointer.
The buffer pointed to by `data` must be at least Bits/8 bytes.
@@ -402,9 +413,21 @@ extern std::size_t hash_value (uint160 const&);
extern std::size_t hash_value (uint256 const&);
//------------------------------------------------------------------------------
template <std::size_t Bits, class Tag>
template <typename Tag1, typename Tag2>
struct is_tag_compatible
{
static bool const value =
std::is_same <Tag1, Tag2>::value ||
std::is_same <Tag1, void>::value ||
std::is_same <Tag2, void>::value;
};
template <std::size_t Bits, class Tag, class OtherTag,
class = std::enable_if_t <is_tag_compatible<Tag, OtherTag>::value>
>
inline int compare (
base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
base_uint<Bits, Tag> const& a, base_uint<Bits, OtherTag> const& b)
{
auto ret = std::mismatch (a.cbegin (), a.cend (), b.cbegin ());
@@ -419,44 +442,56 @@ inline int compare (
return -1;
}
template <std::size_t Bits, class Tag>
template <std::size_t Bits, class Tag, class OtherTag,
class = std::enable_if_t <is_tag_compatible<Tag, OtherTag>::value>
>
inline bool operator< (
base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
base_uint<Bits, Tag> const& a, base_uint<Bits, OtherTag> const& b)
{
return compare (a, b) < 0;
}
template <std::size_t Bits, class Tag>
template <std::size_t Bits, class Tag, class OtherTag,
class = std::enable_if_t <is_tag_compatible<Tag, OtherTag>::value>
>
inline bool operator<= (
base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
base_uint<Bits, Tag> const& a, base_uint<Bits, OtherTag> const& b)
{
return compare (a, b) <= 0;
}
template <std::size_t Bits, class Tag>
template <std::size_t Bits, class Tag, class OtherTag,
class = std::enable_if_t <is_tag_compatible<Tag, OtherTag>::value>
>
inline bool operator> (
base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
base_uint<Bits, Tag> const& a, base_uint<Bits, OtherTag> const& b)
{
return compare (a, b) > 0;
}
template <std::size_t Bits, class Tag>
template <std::size_t Bits, class Tag, class OtherTag,
class = std::enable_if_t <is_tag_compatible<Tag, OtherTag>::value>
>
inline bool operator>= (
base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
base_uint<Bits, Tag> const& a, base_uint<Bits, OtherTag> const& b)
{
return compare (a, b) >= 0;
}
template <std::size_t Bits, class Tag>
template <std::size_t Bits, class Tag, class OtherTag,
class = std::enable_if_t <is_tag_compatible<Tag, OtherTag>::value>
>
inline bool operator== (
base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
base_uint<Bits, Tag> const& a, base_uint<Bits, OtherTag> const& b)
{
return compare (a, b) == 0;
}
template <std::size_t Bits, class Tag>
template <std::size_t Bits, class Tag, class OtherTag,
class = std::enable_if_t <is_tag_compatible<Tag, OtherTag>::value>
>
inline bool operator!= (
base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
base_uint<Bits, Tag> const& a, base_uint<Bits, OtherTag> const& b)
{
return compare (a, b) != 0;
}
@@ -517,6 +552,6 @@ inline std::ostream& operator<< (
return out << to_string (u);
}
}
} // rippled
#endif

View File

@@ -21,10 +21,11 @@
#include <ripple/unity/app.h>
#include <ripple/module/app/book/impl/Taker.cpp>
#include <ripple/module/app/book/impl/BookTip.cpp>
#include <ripple/module/app/book/impl/OfferStream.cpp>
#include <ripple/module/app/book/impl/Quality.cpp>
#include <ripple/module/app/book/impl/Taker.cpp>
#include <ripple/module/app/book/impl/Types.cpp>
#include <ripple/module/app/transactors/Transactor.cpp>