Optimize trust line caching:

The existing trust line caching code was suboptimal in that it stored
redundant information, pinned SLEs into memory and required multiple
memory allocations per cached object.

This commit eliminates redundant data, reducing the size of cached
objects and unpinning SLEs from memory, and uses value_types to
avoid the need for `std::shared_ptr`. As a result of these changes, the
effective size of a cached object, includes the overhead of the memory
allocator and the `std::shared_ptr` should be reduced by at least 64
bytes. This is significant, as there can easily be tens of millions
of these objects.
This commit is contained in:
seelabs
2022-01-23 14:47:51 -05:00
committed by Nik Bougalis
parent 59f5844381
commit 4d5459d041
13 changed files with 255 additions and 214 deletions

View File

@@ -395,7 +395,7 @@ target_sources (rippled PRIVATE
src/ripple/app/paths/Pathfinder.cpp
src/ripple/app/paths/RippleCalc.cpp
src/ripple/app/paths/RippleLineCache.cpp
src/ripple/app/paths/RippleState.cpp
src/ripple/app/paths/TrustLine.cpp
src/ripple/app/paths/impl/BookStep.cpp
src/ripple/app/paths/impl/DirectStep.cpp
src/ripple/app/paths/impl/PaySteps.cpp

View File

@@ -33,24 +33,16 @@ accountSourceCurrencies(
if (includeXRP)
currencies.insert(xrpCurrency());
// List of ripple lines.
auto& rippleLines = lrCache->getRippleLines(account);
for (auto const& item : rippleLines)
for (auto const& rspEntry : lrCache->getRippleLines(account))
{
auto rspEntry = (RippleState*)item.get();
assert(rspEntry);
if (!rspEntry)
continue;
auto& saBalance = rspEntry->getBalance();
auto& saBalance = rspEntry.getBalance();
// Filter out non
if (saBalance > beast::zero
// Have IOUs to send.
|| (rspEntry->getLimitPeer()
|| (rspEntry.getLimitPeer()
// Peer extends credit.
&& ((-saBalance) < rspEntry->getLimitPeer()))) // Credit left.
&& ((-saBalance) < rspEntry.getLimitPeer()))) // Credit left.
{
currencies.insert(saBalance.getCurrency());
}
@@ -72,19 +64,11 @@ accountDestCurrencies(
currencies.insert(xrpCurrency());
// Even if account doesn't exist
// List of ripple lines.
auto& rippleLines = lrCache->getRippleLines(account);
for (auto const& item : rippleLines)
for (auto const& rspEntry : lrCache->getRippleLines(account))
{
auto rspEntry = (RippleState*)item.get();
assert(rspEntry);
if (!rspEntry)
continue;
auto& saBalance = rspEntry.getBalance();
auto& saBalance = rspEntry->getBalance();
if (saBalance < rspEntry->getLimit()) // Can take more
if (saBalance < rspEntry.getLimit()) // Can take more
currencies.insert(saBalance.getCurrency());
}

View File

@@ -717,30 +717,27 @@ Pathfinder::getPathsOut(
{
count = app_.getOrderBookDB().getBookSize(issue);
for (auto const& item : mRLCache->getRippleLines(account))
for (auto const& rspEntry : mRLCache->getRippleLines(account))
{
RippleState* rspEntry = (RippleState*)item.get();
if (currency != rspEntry->getLimit().getCurrency())
if (currency != rspEntry.getLimit().getCurrency())
{
}
else if (
rspEntry->getBalance() <= beast::zero &&
(!rspEntry->getLimitPeer() ||
-rspEntry->getBalance() >= rspEntry->getLimitPeer() ||
(bAuthRequired && !rspEntry->getAuth())))
rspEntry.getBalance() <= beast::zero &&
(!rspEntry.getLimitPeer() ||
-rspEntry.getBalance() >= rspEntry.getLimitPeer() ||
(bAuthRequired && !rspEntry.getAuth())))
{
}
else if (
isDstCurrency && dstAccount == rspEntry->getAccountIDPeer())
else if (isDstCurrency && dstAccount == rspEntry.getAccountIDPeer())
{
count += 10000; // count a path to the destination extra
}
else if (rspEntry->getNoRipplePeer())
else if (rspEntry.getNoRipplePeer())
{
// This probably isn't a useful path out
}
else if (rspEntry->getFreezePeer())
else if (rspEntry.getFreezePeer())
{
// Not a useful path out
}
@@ -940,15 +937,9 @@ Pathfinder::addLink(
AccountCandidates candidates;
candidates.reserve(rippleLines.size());
for (auto const& item : rippleLines)
for (auto const& rs : rippleLines)
{
auto* rs = dynamic_cast<RippleState const*>(item.get());
if (!rs)
{
JLOG(j_.error()) << "Couldn't decipher RippleState";
continue;
}
auto const& acct = rs->getAccountIDPeer();
auto const& acct = rs.getAccountIDPeer();
if (hasEffectiveDestination && (acct == mDstAccount))
{
@@ -963,18 +954,18 @@ Pathfinder::addLink(
continue;
}
if ((uEndCurrency == rs->getLimit().getCurrency()) &&
if ((uEndCurrency == rs.getLimit().getCurrency()) &&
!currentPath.hasSeen(acct, uEndCurrency, acct))
{
// path is for correct currency and has not been seen
if (rs->getBalance() <= beast::zero &&
(!rs->getLimitPeer() ||
-rs->getBalance() >= rs->getLimitPeer() ||
(bRequireAuth && !rs->getAuth())))
if (rs.getBalance() <= beast::zero &&
(!rs.getLimitPeer() ||
-rs.getBalance() >= rs.getLimitPeer() ||
(bRequireAuth && !rs.getAuth())))
{
// path has no credit
}
else if (bIsNoRippleOut && rs->getNoRipple())
else if (bIsNoRippleOut && rs.getNoRipple())
{
// Can't leave on this path
}

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <ripple/app/paths/RippleLineCache.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/ledger/OpenView.h>
namespace ripple {
@@ -30,18 +31,17 @@ RippleLineCache::RippleLineCache(std::shared_ptr<ReadView const> const& ledger)
mLedger = std::make_shared<OpenView>(&*ledger, ledger);
}
std::vector<RippleState::pointer> const&
std::vector<PathFindTrustLine> const&
RippleLineCache::getRippleLines(AccountID const& accountID)
{
AccountKey key(accountID, hasher_(accountID));
std::lock_guard sl(mLock);
auto [it, inserted] =
lines_.emplace(key, std::vector<RippleState::pointer>());
auto [it, inserted] = lines_.emplace(key, std::vector<PathFindTrustLine>());
if (inserted)
it->second = getRippleStateItems(accountID, *mLedger);
it->second = PathFindTrustLine::getItems(accountID, *mLedger);
return it->second;
}

View File

@@ -21,8 +21,10 @@
#define RIPPLE_APP_PATHS_RIPPLELINECACHE_H_INCLUDED
#include <ripple/app/ledger/Ledger.h>
#include <ripple/app/paths/RippleState.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/basics/CountedObject.h>
#include <ripple/basics/hardened_hash.h>
#include <cstddef>
#include <memory>
#include <mutex>
@@ -31,7 +33,7 @@
namespace ripple {
// Used by Pathfinder
class RippleLineCache
class RippleLineCache : public CountedObject<RippleLineCache>
{
public:
explicit RippleLineCache(std::shared_ptr<ReadView const> const& l);
@@ -42,7 +44,7 @@ public:
return mLedger;
}
std::vector<RippleState::pointer> const&
std::vector<PathFindTrustLine> const&
getRippleLines(AccountID const& accountID);
private:
@@ -90,7 +92,7 @@ private:
};
};
hash_map<AccountKey, std::vector<RippleState::pointer>, AccountKey::Hash>
hash_map<AccountKey, std::vector<PathFindTrustLine>, AccountKey::Hash>
lines_;
};

View File

@@ -1,85 +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/app/main/Application.h>
#include <ripple/app/paths/RippleState.h>
#include <ripple/protocol/STAmount.h>
#include <cstdint>
#include <memory>
namespace ripple {
RippleState::pointer
RippleState::makeItem(
AccountID const& accountID,
std::shared_ptr<SLE const> sle)
{
// VFALCO Does this ever happen in practice?
if (!sle || sle->getType() != ltRIPPLE_STATE)
return {};
return std::make_shared<RippleState>(std::move(sle), accountID);
}
RippleState::RippleState(
std::shared_ptr<SLE const>&& sle,
AccountID const& viewAccount)
: sle_(std::move(sle))
, mFlags(sle_->getFieldU32(sfFlags))
, mLowLimit(sle_->getFieldAmount(sfLowLimit))
, mHighLimit(sle_->getFieldAmount(sfHighLimit))
, mLowID(mLowLimit.getIssuer())
, mHighID(mHighLimit.getIssuer())
, lowQualityIn_(sle_->getFieldU32(sfLowQualityIn))
, lowQualityOut_(sle_->getFieldU32(sfLowQualityOut))
, highQualityIn_(sle_->getFieldU32(sfHighQualityIn))
, highQualityOut_(sle_->getFieldU32(sfHighQualityOut))
, mBalance(sle_->getFieldAmount(sfBalance))
{
mViewLowest = (mLowID == viewAccount);
if (!mViewLowest)
mBalance.negate();
}
Json::Value
RippleState::getJson(int)
{
Json::Value ret(Json::objectValue);
ret["low_id"] = to_string(mLowID);
ret["high_id"] = to_string(mHighID);
return ret;
}
std::vector<RippleState::pointer>
getRippleStateItems(AccountID const& accountID, ReadView const& view)
{
std::vector<RippleState::pointer> items;
forEachItem(
view,
accountID,
[&items, &accountID](std::shared_ptr<SLE const> const& sleCur) {
auto ret = RippleState::makeItem(accountID, sleCur);
if (ret)
items.push_back(ret);
});
return items;
}
} // namespace ripple

View File

@@ -0,0 +1,113 @@
//------------------------------------------------------------------------------
/*
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/app/main/Application.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/protocol/STAmount.h>
#include <cstdint>
#include <memory>
namespace ripple {
TrustLineBase::TrustLineBase(
std::shared_ptr<SLE const> const& sle,
AccountID const& viewAccount)
: key_(sle->key())
, mLowLimit(sle->getFieldAmount(sfLowLimit))
, mHighLimit(sle->getFieldAmount(sfHighLimit))
, mBalance(sle->getFieldAmount(sfBalance))
, mFlags(sle->getFieldU32(sfFlags))
, mViewLowest(mLowLimit.getIssuer() == viewAccount)
{
if (!mViewLowest)
mBalance.negate();
}
Json::Value
TrustLineBase::getJson(int)
{
Json::Value ret(Json::objectValue);
ret["low_id"] = to_string(mLowLimit.getIssuer());
ret["high_id"] = to_string(mHighLimit.getIssuer());
return ret;
}
std::optional<PathFindTrustLine>
PathFindTrustLine::makeItem(
AccountID const& accountID,
std::shared_ptr<SLE const> const& sle)
{
if (!sle || sle->getType() != ltRIPPLE_STATE)
return {};
return std::optional{PathFindTrustLine{sle, accountID}};
}
namespace detail {
template <class T>
std::vector<T>
getTrustLineItems(AccountID const& accountID, ReadView const& view)
{
std::vector<T> items;
forEachItem(
view,
accountID,
[&items, &accountID](std::shared_ptr<SLE const> const& sleCur) {
auto ret = T::makeItem(accountID, sleCur);
if (ret)
items.push_back(std::move(*ret));
});
return items;
}
} // namespace detail
std::vector<PathFindTrustLine>
PathFindTrustLine::getItems(AccountID const& accountID, ReadView const& view)
{
return detail::getTrustLineItems<PathFindTrustLine>(accountID, view);
}
RPCTrustLine::RPCTrustLine(
std::shared_ptr<SLE const> const& sle,
AccountID const& viewAccount)
: TrustLineBase(sle, viewAccount)
, lowQualityIn_(sle->getFieldU32(sfLowQualityIn))
, lowQualityOut_(sle->getFieldU32(sfLowQualityOut))
, highQualityIn_(sle->getFieldU32(sfHighQualityIn))
, highQualityOut_(sle->getFieldU32(sfHighQualityOut))
{
}
std::optional<RPCTrustLine>
RPCTrustLine::makeItem(
AccountID const& accountID,
std::shared_ptr<SLE const> const& sle)
{
if (!sle || sle->getType() != ltRIPPLE_STATE)
return {};
return std::optional{RPCTrustLine{sle, accountID}};
}
std::vector<RPCTrustLine>
RPCTrustLine::getItems(AccountID const& accountID, ReadView const& view)
{
return detail::getTrustLineItems<RPCTrustLine>(accountID, view);
}
} // namespace ripple

View File

@@ -20,12 +20,14 @@
#ifndef RIPPLE_APP_PATHS_RIPPLESTATE_H_INCLUDED
#define RIPPLE_APP_PATHS_RIPPLESTATE_H_INCLUDED
#include <ripple/basics/CountedObject.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Rate.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <cstdint>
#include <memory> // <memory>
#include <optional>
namespace ripple {
@@ -34,30 +36,32 @@ namespace ripple {
"low" account and a "high" account. This wraps the
SLE and expresses its data from the perspective of
a chosen account on the line.
This wrapper is primarily used in the path finder and there can easily be
tens of millions of instances of this class. When modifying this class think
carefully about the memory implications.
*/
// VFALCO TODO Rename to TrustLine
class RippleState
class TrustLineBase
{
public:
// VFALCO Why is this shared_ptr?
using pointer = std::shared_ptr<RippleState>;
protected:
// This class should not be instantiated directly. Use one of the derived
// classes.
TrustLineBase(
std::shared_ptr<SLE const> const& sle,
AccountID const& viewAccount);
~TrustLineBase() = default;
TrustLineBase(TrustLineBase const&) = default;
TrustLineBase&
operator=(TrustLineBase const&) = delete;
TrustLineBase(TrustLineBase&&) = default;
public:
RippleState() = delete;
virtual ~RippleState() = default;
static RippleState::pointer
makeItem(AccountID const& accountID, std::shared_ptr<SLE const> sle);
// Must be public, for make_shared
RippleState(std::shared_ptr<SLE const>&& sle, AccountID const& viewAccount);
/** Returns the state map key for the ledger entry. */
uint256
uint256 const&
key() const
{
return sle_->key();
return key_;
}
// VFALCO Take off the "get" from each function name
@@ -65,13 +69,13 @@ public:
AccountID const&
getAccountID() const
{
return mViewLowest ? mLowID : mHighID;
return mViewLowest ? mLowLimit.getIssuer() : mHighLimit.getIssuer();
}
AccountID const&
getAccountIDPeer() const
{
return !mViewLowest ? mLowID : mHighID;
return !mViewLowest ? mLowLimit.getIssuer() : mHighLimit.getIssuer();
}
// True, Provided auth to peer.
@@ -137,6 +141,52 @@ public:
return !mViewLowest ? mLowLimit : mHighLimit;
}
Json::Value
getJson(int);
protected:
uint256 key_;
STAmount const mLowLimit;
STAmount const mHighLimit;
STAmount mBalance;
std::uint32_t mFlags;
bool mViewLowest;
};
// This wrapper is used for the path finder
class PathFindTrustLine final : public TrustLineBase,
public CountedObject<PathFindTrustLine>
{
using TrustLineBase::TrustLineBase;
public:
PathFindTrustLine() = delete;
static std::optional<PathFindTrustLine>
makeItem(AccountID const& accountID, std::shared_ptr<SLE const> const& sle);
static std::vector<PathFindTrustLine>
getItems(AccountID const& accountID, ReadView const& view);
};
// This wrapper is used for the `AccountLines` command and includes the quality
// in and quality out values.
class RPCTrustLine final : public TrustLineBase,
public CountedObject<RPCTrustLine>
{
using TrustLineBase::TrustLineBase;
public:
RPCTrustLine() = delete;
RPCTrustLine(
std::shared_ptr<SLE const> const& sle,
AccountID const& viewAccount);
Rate const&
getQualityIn() const
{
@@ -149,33 +199,19 @@ public:
return mViewLowest ? lowQualityOut_ : highQualityOut_;
}
Json::Value
getJson(int);
static std::optional<RPCTrustLine>
makeItem(AccountID const& accountID, std::shared_ptr<SLE const> const& sle);
static std::vector<RPCTrustLine>
getItems(AccountID const& accountID, ReadView const& view);
private:
std::shared_ptr<SLE const> sle_;
bool mViewLowest;
std::uint32_t mFlags;
STAmount const& mLowLimit;
STAmount const& mHighLimit;
AccountID const& mLowID;
AccountID const& mHighID;
Rate lowQualityIn_;
Rate lowQualityOut_;
Rate highQualityIn_;
Rate highQualityOut_;
STAmount mBalance;
};
std::vector<RippleState::pointer>
getRippleStateItems(AccountID const& accountID, ReadView const& view);
} // namespace ripple
#endif

View File

@@ -18,7 +18,7 @@
//==============================================================================
#include <ripple/app/main/Application.h>
#include <ripple/app/paths/RippleState.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
@@ -58,15 +58,13 @@ doAccountCurrencies(RPC::JsonContext& context)
return rpcError(rpcACT_NOT_FOUND);
std::set<Currency> send, receive;
for (auto const& item : getRippleStateItems(accountID, *ledger))
for (auto const& rspEntry : RPCTrustLine::getItems(accountID, *ledger))
{
auto const rspEntry = item.get();
STAmount const& saBalance = rspEntry.getBalance();
STAmount const& saBalance = rspEntry->getBalance();
if (saBalance < rspEntry->getLimit())
if (saBalance < rspEntry.getLimit())
receive.insert(saBalance.getCurrency());
if ((-saBalance) < rspEntry->getLimitPeer())
if ((-saBalance) < rspEntry.getLimitPeer())
send.insert(saBalance.getCurrency());
}

View File

@@ -18,7 +18,7 @@
//==============================================================================
#include <ripple/app/main/Application.h>
#include <ripple/app/paths/RippleState.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
@@ -32,18 +32,17 @@ namespace ripple {
struct VisitData
{
std::vector<RippleState::pointer> items;
std::vector<RPCTrustLine> items;
AccountID const& accountID;
bool hasPeer;
AccountID const& raPeerAccount;
bool ignoreDefault;
uint32_t foundCount;
RippleState::pointer lastFound;
};
void
addLine(Json::Value& jsonLines, RippleState const& line)
addLine(Json::Value& jsonLines, RPCTrustLine const& line)
{
STAmount const& saBalance(line.getBalance());
STAmount const& saLimit(line.getLimit());
@@ -140,7 +139,7 @@ doAccountLines(RPC::JsonContext& context)
Json::Value& jsonLines(result[jss::lines] = Json::arrayValue);
VisitData visitData = {
{}, accountID, hasPeer, raPeerAccount, ignoreDefault, 0, nullptr};
{}, accountID, hasPeer, raPeerAccount, ignoreDefault, 0};
uint256 startAfter = beast::zero;
std::uint64_t startHint = 0;
@@ -194,18 +193,6 @@ doAccountLines(RPC::JsonContext& context)
limit + 1,
[&visitData, &count, &marker, &limit, &nextHint](
std::shared_ptr<SLE const> const& sleCur) {
bool ignore = false;
if (visitData.ignoreDefault)
{
if (sleCur->getFieldAmount(sfLowLimit).getIssuer() ==
visitData.accountID)
ignore =
!(sleCur->getFieldU32(sfFlags) & lsfLowReserve);
else
ignore = !(
sleCur->getFieldU32(sfFlags) & lsfHighReserve);
}
if (!sleCur)
{
assert(false);
@@ -219,17 +206,32 @@ doAccountLines(RPC::JsonContext& context)
RPC::getStartHint(sleCur, visitData.accountID);
}
if (sleCur->getType() != ltRIPPLE_STATE)
return true;
bool ignore = false;
if (visitData.ignoreDefault)
{
if (sleCur->getFieldAmount(sfLowLimit).getIssuer() ==
visitData.accountID)
ignore =
!(sleCur->getFieldU32(sfFlags) & lsfLowReserve);
else
ignore = !(
sleCur->getFieldU32(sfFlags) & lsfHighReserve);
}
if (!ignore && count <= limit)
{
auto const line =
RippleState::makeItem(visitData.accountID, sleCur);
RPCTrustLine::makeItem(visitData.accountID, sleCur);
if (line != nullptr &&
if (line &&
(!visitData.hasPeer ||
visitData.raPeerAccount ==
line->getAccountIDPeer()))
{
visitData.items.emplace_back(line);
visitData.items.emplace_back(*line);
}
}
@@ -253,7 +255,7 @@ doAccountLines(RPC::JsonContext& context)
result[jss::account] = context.app.accountIDCache().toBase58(accountID);
for (auto const& item : visitData.items)
addLine(jsonLines, *item.get());
addLine(jsonLines, item);
context.loadType = Resource::feeMediumBurdenRPC;
return result;

View File

@@ -18,7 +18,7 @@
//==============================================================================
#include <ripple/app/main/Application.h>
#include <ripple/app/paths/RippleState.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/ErrorCodes.h>
@@ -144,7 +144,7 @@ doGatewayBalances(RPC::JsonContext& context)
{
forEachItem(
*ledger, accountID, [&](std::shared_ptr<SLE const> const& sle) {
auto rs = RippleState::makeItem(accountID, sle);
auto rs = PathFindTrustLine::makeItem(accountID, sle);
if (!rs)
return;

View File

@@ -19,7 +19,7 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/paths/RippleState.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>

View File

@@ -20,7 +20,7 @@
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/ledger/OpenLedger.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/paths/RippleState.h>
#include <ripple/app/paths/TrustLine.h>
#include <ripple/app/rdb/RelationalDBInterface.h>
#include <ripple/ledger/View.h>
#include <ripple/net/RPCErr.h>