mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 18:45:52 +00:00
This PR splits `RPCHelpers.h` into two files, by moving out all the ledger-fetching-related functions into a separate file, `RPCLedgerHelpers.h`. It also moves `getAccountObjects` to `AccountObjects.h`, since it is only used in that one place.
244 lines
7.9 KiB
C++
244 lines
7.9 KiB
C++
#include <xrpld/app/paths/TrustLine.h>
|
|
#include <xrpld/rpc/Context.h>
|
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
|
#include <xrpld/rpc/detail/Tuning.h>
|
|
|
|
#include <xrpl/ledger/ReadView.h>
|
|
#include <xrpl/protocol/ErrorCodes.h>
|
|
#include <xrpl/protocol/RPCErr.h>
|
|
#include <xrpl/protocol/jss.h>
|
|
#include <xrpl/resource/Fees.h>
|
|
|
|
namespace ripple {
|
|
|
|
void
|
|
addLine(Json::Value& jsonLines, RPCTrustLine const& line)
|
|
{
|
|
STAmount const& saBalance(line.getBalance());
|
|
STAmount const& saLimit(line.getLimit());
|
|
STAmount const& saLimitPeer(line.getLimitPeer());
|
|
Json::Value& jPeer(jsonLines.append(Json::objectValue));
|
|
|
|
jPeer[jss::account] = to_string(line.getAccountIDPeer());
|
|
// Amount reported is positive if current account holds other
|
|
// account's IOUs.
|
|
//
|
|
// Amount reported is negative if other account holds current
|
|
// account's IOUs.
|
|
jPeer[jss::balance] = saBalance.getText();
|
|
jPeer[jss::currency] = to_string(saBalance.issue().currency);
|
|
jPeer[jss::limit] = saLimit.getText();
|
|
jPeer[jss::limit_peer] = saLimitPeer.getText();
|
|
jPeer[jss::quality_in] = line.getQualityIn().value;
|
|
jPeer[jss::quality_out] = line.getQualityOut().value;
|
|
if (line.getAuth())
|
|
jPeer[jss::authorized] = true;
|
|
if (line.getAuthPeer())
|
|
jPeer[jss::peer_authorized] = true;
|
|
if (line.getNoRipple())
|
|
jPeer[jss::no_ripple] = true;
|
|
if (line.getNoRipplePeer())
|
|
jPeer[jss::no_ripple_peer] = true;
|
|
if (line.getFreeze())
|
|
jPeer[jss::freeze] = true;
|
|
if (line.getFreezePeer())
|
|
jPeer[jss::freeze_peer] = true;
|
|
if (line.getDeepFreeze())
|
|
jPeer[jss::deep_freeze] = true;
|
|
if (line.getDeepFreezePeer())
|
|
jPeer[jss::deep_freeze_peer] = true;
|
|
}
|
|
|
|
// {
|
|
// account: <account>
|
|
// ledger_hash : <ledger>
|
|
// ledger_index : <ledger_index>
|
|
// limit: integer // optional
|
|
// marker: opaque // optional, resume previous query
|
|
// ignore_default: bool // do not return lines in default state (on
|
|
// this account's side)
|
|
// }
|
|
Json::Value
|
|
doAccountLines(RPC::JsonContext& context)
|
|
{
|
|
auto const& params(context.params);
|
|
if (!params.isMember(jss::account))
|
|
return RPC::missing_field_error(jss::account);
|
|
|
|
if (!params[jss::account].isString())
|
|
return RPC::invalid_field_error(jss::account);
|
|
|
|
std::shared_ptr<ReadView const> ledger;
|
|
auto result = RPC::lookupLedger(ledger, context);
|
|
if (!ledger)
|
|
return result;
|
|
|
|
auto id = parseBase58<AccountID>(params[jss::account].asString());
|
|
if (!id)
|
|
{
|
|
RPC::inject_error(rpcACT_MALFORMED, result);
|
|
return result;
|
|
}
|
|
auto const accountID{std::move(id.value())};
|
|
|
|
if (!ledger->exists(keylet::account(accountID)))
|
|
return rpcError(rpcACT_NOT_FOUND);
|
|
|
|
std::string strPeer;
|
|
if (params.isMember(jss::peer))
|
|
strPeer = params[jss::peer].asString();
|
|
|
|
auto const raPeerAccount = [&]() -> std::optional<AccountID> {
|
|
return strPeer.empty() ? std::nullopt : parseBase58<AccountID>(strPeer);
|
|
}();
|
|
if (!strPeer.empty() && !raPeerAccount)
|
|
{
|
|
RPC::inject_error(rpcACT_MALFORMED, result);
|
|
return result;
|
|
}
|
|
|
|
unsigned int limit;
|
|
if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context))
|
|
return *err;
|
|
|
|
// this flag allows the requester to ask incoming trustlines in default
|
|
// state be omitted
|
|
bool ignoreDefault = params.isMember(jss::ignore_default) &&
|
|
params[jss::ignore_default].asBool();
|
|
|
|
Json::Value& jsonLines(result[jss::lines] = Json::arrayValue);
|
|
struct VisitData
|
|
{
|
|
std::vector<RPCTrustLine> items;
|
|
AccountID const& accountID;
|
|
std::optional<AccountID> const& raPeerAccount;
|
|
bool ignoreDefault;
|
|
uint32_t foundCount;
|
|
};
|
|
VisitData visitData = {{}, accountID, raPeerAccount, ignoreDefault, 0};
|
|
uint256 startAfter = beast::zero;
|
|
std::uint64_t startHint = 0;
|
|
|
|
if (params.isMember(jss::marker))
|
|
{
|
|
if (!params[jss::marker].isString())
|
|
return RPC::expected_field_error(jss::marker, "string");
|
|
|
|
// Marker is composed of a comma separated index and start hint. The
|
|
// former will be read as hex, and the latter using boost lexical cast.
|
|
std::stringstream marker(params[jss::marker].asString());
|
|
std::string value;
|
|
if (!std::getline(marker, value, ','))
|
|
return rpcError(rpcINVALID_PARAMS);
|
|
|
|
if (!startAfter.parseHex(value))
|
|
return rpcError(rpcINVALID_PARAMS);
|
|
|
|
if (!std::getline(marker, value, ','))
|
|
return rpcError(rpcINVALID_PARAMS);
|
|
|
|
try
|
|
{
|
|
startHint = boost::lexical_cast<std::uint64_t>(value);
|
|
}
|
|
catch (boost::bad_lexical_cast&)
|
|
{
|
|
return rpcError(rpcINVALID_PARAMS);
|
|
}
|
|
|
|
// We then must check if the object pointed to by the marker is actually
|
|
// owned by the account in the request.
|
|
auto const sle = ledger->read({ltANY, startAfter});
|
|
|
|
if (!sle)
|
|
return rpcError(rpcINVALID_PARAMS);
|
|
|
|
if (!RPC::isRelatedToAccount(*ledger, sle, accountID))
|
|
return rpcError(rpcINVALID_PARAMS);
|
|
}
|
|
|
|
auto count = 0;
|
|
std::optional<uint256> marker = {};
|
|
std::uint64_t nextHint = 0;
|
|
{
|
|
if (!forEachItemAfter(
|
|
*ledger,
|
|
accountID,
|
|
startAfter,
|
|
startHint,
|
|
limit + 1,
|
|
[&visitData, &count, &marker, &limit, &nextHint](
|
|
std::shared_ptr<SLE const> const& sleCur) {
|
|
if (!sleCur)
|
|
{
|
|
// LCOV_EXCL_START
|
|
UNREACHABLE("ripple::doAccountLines : null SLE");
|
|
return false;
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
if (++count == limit)
|
|
{
|
|
marker = sleCur->key();
|
|
nextHint =
|
|
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 =
|
|
RPCTrustLine::makeItem(visitData.accountID, sleCur);
|
|
|
|
if (line &&
|
|
(!visitData.raPeerAccount ||
|
|
*visitData.raPeerAccount ==
|
|
line->getAccountIDPeer()))
|
|
{
|
|
visitData.items.emplace_back(*line);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}))
|
|
{
|
|
return rpcError(rpcINVALID_PARAMS);
|
|
}
|
|
}
|
|
|
|
// Both conditions need to be checked because marker is set on the limit-th
|
|
// item, but if there is no item on the limit + 1 iteration, then there is
|
|
// no need to return a marker.
|
|
if (count == limit + 1 && marker)
|
|
{
|
|
result[jss::limit] = limit;
|
|
result[jss::marker] =
|
|
to_string(*marker) + "," + std::to_string(nextHint);
|
|
}
|
|
|
|
result[jss::account] = toBase58(accountID);
|
|
|
|
for (auto const& item : visitData.items)
|
|
addLine(jsonLines, item);
|
|
|
|
context.loadType = Resource::feeMediumBurdenRPC;
|
|
return result;
|
|
}
|
|
|
|
} // namespace ripple
|