mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-27 00:50:45 +00:00
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:
committed by
Vinnie Falco
parent
a23013abc1
commit
e24cba8c35
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
77
src/ripple/basics/utility/StringConcat.h
Normal file
77
src/ripple/basics/utility/StringConcat.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
61
src/ripple/basics/utility/ToString.h
Normal file
61
src/ripple/basics/utility/ToString.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
29
src/ripple/module/app/book/impl/Types.cpp
Normal file
29
src/ripple/module/app/book/impl/Types.cpp
Normal 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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
111
src/ripple/module/app/paths/ComputeOfferLiquidity.cpp
Normal file
111
src/ripple/module/app/paths/ComputeOfferLiquidity.cpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
src/ripple/module/app/paths/Types.cpp
Normal file
27
src/ripple/module/app/paths/Types.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: " <<
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user