mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-23 12:35:50 +00:00
refactor: split up RPCHelpers.h into two (#6047)
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.
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
#include <xrpld/app/misc/Transaction.h>
|
#include <xrpld/app/misc/Transaction.h>
|
||||||
#include <xrpld/core/Config.h>
|
#include <xrpld/core/Config.h>
|
||||||
#include <xrpld/core/DatabaseCon.h>
|
#include <xrpld/core/DatabaseCon.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
|
|
||||||
|
|||||||
@@ -130,513 +130,6 @@ isRelatedToAccount(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
getAccountObjects(
|
|
||||||
ReadView const& ledger,
|
|
||||||
AccountID const& account,
|
|
||||||
std::optional<std::vector<LedgerEntryType>> const& typeFilter,
|
|
||||||
uint256 dirIndex,
|
|
||||||
uint256 entryIndex,
|
|
||||||
std::uint32_t const limit,
|
|
||||||
Json::Value& jvResult)
|
|
||||||
{
|
|
||||||
// check if dirIndex is valid
|
|
||||||
if (!dirIndex.isZero() && !ledger.read({ltDIR_NODE, dirIndex}))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto typeMatchesFilter = [](std::vector<LedgerEntryType> const& typeFilter,
|
|
||||||
LedgerEntryType ledgerType) {
|
|
||||||
auto it = std::find(typeFilter.begin(), typeFilter.end(), ledgerType);
|
|
||||||
return it != typeFilter.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
// if dirIndex != 0, then all NFTs have already been returned. only
|
|
||||||
// iterate NFT pages if the filter says so AND dirIndex == 0
|
|
||||||
bool iterateNFTPages =
|
|
||||||
(!typeFilter.has_value() ||
|
|
||||||
typeMatchesFilter(typeFilter.value(), ltNFTOKEN_PAGE)) &&
|
|
||||||
dirIndex == beast::zero;
|
|
||||||
|
|
||||||
Keylet const firstNFTPage = keylet::nftpage_min(account);
|
|
||||||
|
|
||||||
// we need to check the marker to see if it is an NFTTokenPage index.
|
|
||||||
if (iterateNFTPages && entryIndex != beast::zero)
|
|
||||||
{
|
|
||||||
// if it is we will try to iterate the pages up to the limit
|
|
||||||
// and then change over to the owner directory
|
|
||||||
|
|
||||||
if (firstNFTPage.key != (entryIndex & ~nft::pageMask))
|
|
||||||
iterateNFTPages = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue);
|
|
||||||
|
|
||||||
// this is a mutable version of limit, used to seamlessly switch
|
|
||||||
// to iterating directory entries when nftokenpages are exhausted
|
|
||||||
uint32_t mlimit = limit;
|
|
||||||
|
|
||||||
// iterate NFTokenPages preferentially
|
|
||||||
if (iterateNFTPages)
|
|
||||||
{
|
|
||||||
Keylet const first = entryIndex == beast::zero
|
|
||||||
? firstNFTPage
|
|
||||||
: Keylet{ltNFTOKEN_PAGE, entryIndex};
|
|
||||||
|
|
||||||
Keylet const last = keylet::nftpage_max(account);
|
|
||||||
|
|
||||||
// current key
|
|
||||||
uint256 ck = ledger.succ(first.key, last.key.next()).value_or(last.key);
|
|
||||||
|
|
||||||
// current page
|
|
||||||
auto cp = ledger.read(Keylet{ltNFTOKEN_PAGE, ck});
|
|
||||||
|
|
||||||
while (cp)
|
|
||||||
{
|
|
||||||
jvObjects.append(cp->getJson(JsonOptions::none));
|
|
||||||
auto const npm = (*cp)[~sfNextPageMin];
|
|
||||||
if (npm)
|
|
||||||
cp = ledger.read(Keylet(ltNFTOKEN_PAGE, *npm));
|
|
||||||
else
|
|
||||||
cp = nullptr;
|
|
||||||
|
|
||||||
if (--mlimit == 0)
|
|
||||||
{
|
|
||||||
if (cp)
|
|
||||||
{
|
|
||||||
jvResult[jss::limit] = limit;
|
|
||||||
jvResult[jss::marker] = std::string("0,") + to_string(ck);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!npm)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ck = *npm;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if execution reaches here then we're about to transition
|
|
||||||
// to iterating the root directory (and the conventional
|
|
||||||
// behaviour of this RPC function.) Therefore we should
|
|
||||||
// zero entryIndex so as not to terribly confuse things.
|
|
||||||
entryIndex = beast::zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const root = keylet::ownerDir(account);
|
|
||||||
auto found = false;
|
|
||||||
|
|
||||||
if (dirIndex.isZero())
|
|
||||||
{
|
|
||||||
dirIndex = root.key;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dir = ledger.read({ltDIR_NODE, dirIndex});
|
|
||||||
if (!dir)
|
|
||||||
{
|
|
||||||
// it's possible the user had nftoken pages but no
|
|
||||||
// directory entries. If there's no nftoken page, we will
|
|
||||||
// give empty array for account_objects.
|
|
||||||
if (mlimit >= limit)
|
|
||||||
jvResult[jss::account_objects] = Json::arrayValue;
|
|
||||||
|
|
||||||
// non-zero dirIndex validity was checked in the beginning of this
|
|
||||||
// function; by this point, it should be zero. This function returns
|
|
||||||
// true regardless of nftoken page presence; if absent, account_objects
|
|
||||||
// is already set as an empty array. Notice we will only return false in
|
|
||||||
// this function when entryIndex can not be found, indicating an invalid
|
|
||||||
// marker error.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t i = 0;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
auto const& entries = dir->getFieldV256(sfIndexes);
|
|
||||||
auto iter = entries.begin();
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
iter = std::find(iter, entries.end(), entryIndex);
|
|
||||||
if (iter == entries.end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's possible that the returned NFTPages exactly filled the
|
|
||||||
// response. Check for that condition.
|
|
||||||
if (i == mlimit && mlimit < limit)
|
|
||||||
{
|
|
||||||
jvResult[jss::limit] = limit;
|
|
||||||
jvResult[jss::marker] =
|
|
||||||
to_string(dirIndex) + ',' + to_string(*iter);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; iter != entries.end(); ++iter)
|
|
||||||
{
|
|
||||||
auto const sleNode = ledger.read(keylet::child(*iter));
|
|
||||||
|
|
||||||
if (!typeFilter.has_value() ||
|
|
||||||
typeMatchesFilter(typeFilter.value(), sleNode->getType()))
|
|
||||||
{
|
|
||||||
jvObjects.append(sleNode->getJson(JsonOptions::none));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++i == mlimit)
|
|
||||||
{
|
|
||||||
if (++iter != entries.end())
|
|
||||||
{
|
|
||||||
jvResult[jss::limit] = limit;
|
|
||||||
jvResult[jss::marker] =
|
|
||||||
to_string(dirIndex) + ',' + to_string(*iter);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const nodeIndex = dir->getFieldU64(sfIndexNext);
|
|
||||||
if (nodeIndex == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
dirIndex = keylet::page(root, nodeIndex).key;
|
|
||||||
dir = ledger.read({ltDIR_NODE, dirIndex});
|
|
||||||
if (!dir)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (i == mlimit)
|
|
||||||
{
|
|
||||||
auto const& e = dir->getFieldV256(sfIndexes);
|
|
||||||
if (!e.empty())
|
|
||||||
{
|
|
||||||
jvResult[jss::limit] = limit;
|
|
||||||
jvResult[jss::marker] =
|
|
||||||
to_string(dirIndex) + ',' + to_string(*e.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
bool
|
|
||||||
isValidatedOld(LedgerMaster& ledgerMaster, bool standalone)
|
|
||||||
{
|
|
||||||
if (standalone)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return ledgerMaster.getValidatedLedgerAge() > Tuning::maxValidatedLedgerAge;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
ledgerFromRequest(T& ledger, JsonContext& context)
|
|
||||||
{
|
|
||||||
ledger.reset();
|
|
||||||
|
|
||||||
auto& params = context.params;
|
|
||||||
|
|
||||||
auto indexValue = params[jss::ledger_index];
|
|
||||||
auto hashValue = params[jss::ledger_hash];
|
|
||||||
|
|
||||||
// We need to support the legacy "ledger" field.
|
|
||||||
auto& legacyLedger = params[jss::ledger];
|
|
||||||
if (legacyLedger)
|
|
||||||
{
|
|
||||||
if (legacyLedger.asString().size() > 12)
|
|
||||||
hashValue = legacyLedger;
|
|
||||||
else
|
|
||||||
indexValue = legacyLedger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hashValue.isNull())
|
|
||||||
{
|
|
||||||
if (!hashValue.isString())
|
|
||||||
return {rpcINVALID_PARAMS, "ledgerHashNotString"};
|
|
||||||
|
|
||||||
uint256 ledgerHash;
|
|
||||||
if (!ledgerHash.parseHex(hashValue.asString()))
|
|
||||||
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
|
||||||
return getLedger(ledger, ledgerHash, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!indexValue.isConvertibleTo(Json::stringValue))
|
|
||||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
|
||||||
|
|
||||||
auto const index = indexValue.asString();
|
|
||||||
|
|
||||||
if (index == "current" || index.empty())
|
|
||||||
return getLedger(ledger, LedgerShortcut::CURRENT, context);
|
|
||||||
|
|
||||||
if (index == "validated")
|
|
||||||
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
|
|
||||||
|
|
||||||
if (index == "closed")
|
|
||||||
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
|
||||||
|
|
||||||
std::uint32_t val;
|
|
||||||
if (!beast::lexicalCastChecked(val, index))
|
|
||||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
|
||||||
|
|
||||||
return getLedger(ledger, val, context);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
template <class T, class R>
|
|
||||||
Status
|
|
||||||
ledgerFromRequest(T& ledger, GRPCContext<R>& context)
|
|
||||||
{
|
|
||||||
R& request = context.params;
|
|
||||||
return ledgerFromSpecifier(ledger, request.ledger(), context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// explicit instantiation of above function
|
|
||||||
template Status
|
|
||||||
ledgerFromRequest<>(
|
|
||||||
std::shared_ptr<ReadView const>&,
|
|
||||||
GRPCContext<org::xrpl::rpc::v1::GetLedgerEntryRequest>&);
|
|
||||||
|
|
||||||
// explicit instantiation of above function
|
|
||||||
template Status
|
|
||||||
ledgerFromRequest<>(
|
|
||||||
std::shared_ptr<ReadView const>&,
|
|
||||||
GRPCContext<org::xrpl::rpc::v1::GetLedgerDataRequest>&);
|
|
||||||
|
|
||||||
// explicit instantiation of above function
|
|
||||||
template Status
|
|
||||||
ledgerFromRequest<>(
|
|
||||||
std::shared_ptr<ReadView const>&,
|
|
||||||
GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest>&);
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
ledgerFromSpecifier(
|
|
||||||
T& ledger,
|
|
||||||
org::xrpl::rpc::v1::LedgerSpecifier const& specifier,
|
|
||||||
Context& context)
|
|
||||||
{
|
|
||||||
ledger.reset();
|
|
||||||
|
|
||||||
using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
|
|
||||||
LedgerCase ledgerCase = specifier.ledger_case();
|
|
||||||
switch (ledgerCase)
|
|
||||||
{
|
|
||||||
case LedgerCase::kHash: {
|
|
||||||
if (auto hash = uint256::fromVoidChecked(specifier.hash()))
|
|
||||||
{
|
|
||||||
return getLedger(ledger, *hash, context);
|
|
||||||
}
|
|
||||||
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
|
||||||
}
|
|
||||||
case LedgerCase::kSequence:
|
|
||||||
return getLedger(ledger, specifier.sequence(), context);
|
|
||||||
case LedgerCase::kShortcut:
|
|
||||||
[[fallthrough]];
|
|
||||||
case LedgerCase::LEDGER_NOT_SET: {
|
|
||||||
auto const shortcut = specifier.shortcut();
|
|
||||||
if (shortcut ==
|
|
||||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
|
|
||||||
{
|
|
||||||
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (shortcut ==
|
|
||||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT ||
|
|
||||||
shortcut ==
|
|
||||||
org::xrpl::rpc::v1::LedgerSpecifier::
|
|
||||||
SHORTCUT_UNSPECIFIED)
|
|
||||||
{
|
|
||||||
return getLedger(ledger, LedgerShortcut::CURRENT, context);
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
shortcut ==
|
|
||||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
|
|
||||||
{
|
|
||||||
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
getLedger(T& ledger, uint256 const& ledgerHash, Context& context)
|
|
||||||
{
|
|
||||||
ledger = context.ledgerMaster.getLedgerByHash(ledgerHash);
|
|
||||||
if (ledger == nullptr)
|
|
||||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
getLedger(T& ledger, uint32_t ledgerIndex, Context& context)
|
|
||||||
{
|
|
||||||
ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex);
|
|
||||||
if (ledger == nullptr)
|
|
||||||
{
|
|
||||||
auto cur = context.ledgerMaster.getCurrentLedger();
|
|
||||||
if (cur->info().seq == ledgerIndex)
|
|
||||||
{
|
|
||||||
ledger = cur;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ledger == nullptr)
|
|
||||||
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
|
||||||
|
|
||||||
if (ledger->info().seq > context.ledgerMaster.getValidLedgerIndex() &&
|
|
||||||
isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
|
|
||||||
{
|
|
||||||
ledger.reset();
|
|
||||||
if (context.apiVersion == 1)
|
|
||||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
|
||||||
return {rpcNOT_SYNCED, "notSynced"};
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
getLedger(T& ledger, LedgerShortcut shortcut, Context& context)
|
|
||||||
{
|
|
||||||
if (isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
|
|
||||||
{
|
|
||||||
if (context.apiVersion == 1)
|
|
||||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
|
||||||
return {rpcNOT_SYNCED, "notSynced"};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortcut == LedgerShortcut::VALIDATED)
|
|
||||||
{
|
|
||||||
ledger = context.ledgerMaster.getValidatedLedger();
|
|
||||||
if (ledger == nullptr)
|
|
||||||
{
|
|
||||||
if (context.apiVersion == 1)
|
|
||||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
|
||||||
return {rpcNOT_SYNCED, "notSynced"};
|
|
||||||
}
|
|
||||||
|
|
||||||
XRPL_ASSERT(
|
|
||||||
!ledger->open(), "ripple::RPC::getLedger : validated is not open");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (shortcut == LedgerShortcut::CURRENT)
|
|
||||||
{
|
|
||||||
ledger = context.ledgerMaster.getCurrentLedger();
|
|
||||||
XRPL_ASSERT(
|
|
||||||
ledger->open(), "ripple::RPC::getLedger : current is open");
|
|
||||||
}
|
|
||||||
else if (shortcut == LedgerShortcut::CLOSED)
|
|
||||||
{
|
|
||||||
ledger = context.ledgerMaster.getClosedLedger();
|
|
||||||
XRPL_ASSERT(
|
|
||||||
!ledger->open(), "ripple::RPC::getLedger : closed is not open");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ledger == nullptr)
|
|
||||||
{
|
|
||||||
if (context.apiVersion == 1)
|
|
||||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
|
||||||
return {rpcNOT_SYNCED, "notSynced"};
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto const minSequenceGap = 10;
|
|
||||||
|
|
||||||
if (ledger->info().seq + minSequenceGap <
|
|
||||||
context.ledgerMaster.getValidLedgerIndex())
|
|
||||||
{
|
|
||||||
ledger.reset();
|
|
||||||
if (context.apiVersion == 1)
|
|
||||||
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
|
||||||
return {rpcNOT_SYNCED, "notSynced"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicit instantiation of above three functions
|
|
||||||
template Status
|
|
||||||
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context&);
|
|
||||||
|
|
||||||
template Status
|
|
||||||
getLedger<>(
|
|
||||||
std::shared_ptr<ReadView const>&,
|
|
||||||
LedgerShortcut shortcut,
|
|
||||||
Context&);
|
|
||||||
|
|
||||||
template Status
|
|
||||||
getLedger<>(std::shared_ptr<ReadView const>&, uint256 const&, Context&);
|
|
||||||
|
|
||||||
// The previous version of the lookupLedger command would accept the
|
|
||||||
// "ledger_index" argument as a string and silently treat it as a request to
|
|
||||||
// return the current ledger which, while not strictly wrong, could cause a lot
|
|
||||||
// of confusion.
|
|
||||||
//
|
|
||||||
// The code now robustly validates the input and ensures that the only possible
|
|
||||||
// values for the "ledger_index" parameter are the index of a ledger passed as
|
|
||||||
// an integer or one of the strings "current", "closed" or "validated".
|
|
||||||
// Additionally, the code ensures that the value passed in "ledger_hash" is a
|
|
||||||
// string and a valid hash. Invalid values will return an appropriate error
|
|
||||||
// code.
|
|
||||||
//
|
|
||||||
// In the absence of the "ledger_hash" or "ledger_index" parameters, the code
|
|
||||||
// assumes that "ledger_index" has the value "current".
|
|
||||||
//
|
|
||||||
// Returns a Json::objectValue. If there was an error, it will be in that
|
|
||||||
// return value. Otherwise, the object contains the field "validated" and
|
|
||||||
// optionally the fields "ledger_hash", "ledger_index" and
|
|
||||||
// "ledger_current_index", if they are defined.
|
|
||||||
Status
|
|
||||||
lookupLedger(
|
|
||||||
std::shared_ptr<ReadView const>& ledger,
|
|
||||||
JsonContext& context,
|
|
||||||
Json::Value& result)
|
|
||||||
{
|
|
||||||
if (auto status = ledgerFromRequest(ledger, context))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
auto& info = ledger->info();
|
|
||||||
|
|
||||||
if (!ledger->open())
|
|
||||||
{
|
|
||||||
result[jss::ledger_hash] = to_string(info.hash);
|
|
||||||
result[jss::ledger_index] = info.seq;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result[jss::ledger_current_index] = info.seq;
|
|
||||||
}
|
|
||||||
|
|
||||||
result[jss::validated] = context.ledgerMaster.isValidated(*ledger);
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
lookupLedger(std::shared_ptr<ReadView const>& ledger, JsonContext& context)
|
|
||||||
{
|
|
||||||
Json::Value result;
|
|
||||||
if (auto status = lookupLedger(ledger, context, result))
|
|
||||||
status.inject(result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_set<AccountID>
|
hash_set<AccountID>
|
||||||
parseAccountIds(Json::Value const& jvArray)
|
parseAccountIds(Json::Value const& jvArray)
|
||||||
{
|
{
|
||||||
@@ -988,123 +481,5 @@ isAccountObjectsValidType(LedgerEntryType const& type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<std::shared_ptr<Ledger const>, Json::Value>
|
|
||||||
getLedgerByContext(RPC::JsonContext& context)
|
|
||||||
{
|
|
||||||
auto const hasHash = context.params.isMember(jss::ledger_hash);
|
|
||||||
auto const hasIndex = context.params.isMember(jss::ledger_index);
|
|
||||||
std::uint32_t ledgerIndex = 0;
|
|
||||||
|
|
||||||
auto& ledgerMaster = context.app.getLedgerMaster();
|
|
||||||
LedgerHash ledgerHash;
|
|
||||||
|
|
||||||
if ((hasHash && hasIndex) || !(hasHash || hasIndex))
|
|
||||||
{
|
|
||||||
return RPC::make_param_error(
|
|
||||||
"Exactly one of ledger_hash and ledger_index can be set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.loadType = Resource::feeHeavyBurdenRPC;
|
|
||||||
|
|
||||||
if (hasHash)
|
|
||||||
{
|
|
||||||
auto const& jsonHash = context.params[jss::ledger_hash];
|
|
||||||
if (!jsonHash.isString() || !ledgerHash.parseHex(jsonHash.asString()))
|
|
||||||
return RPC::invalid_field_error(jss::ledger_hash);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto const& jsonIndex = context.params[jss::ledger_index];
|
|
||||||
if (!jsonIndex.isInt())
|
|
||||||
return RPC::invalid_field_error(jss::ledger_index);
|
|
||||||
|
|
||||||
// We need a validated ledger to get the hash from the sequence
|
|
||||||
if (ledgerMaster.getValidatedLedgerAge() >
|
|
||||||
RPC::Tuning::maxValidatedLedgerAge)
|
|
||||||
{
|
|
||||||
if (context.apiVersion == 1)
|
|
||||||
return rpcError(rpcNO_CURRENT);
|
|
||||||
return rpcError(rpcNOT_SYNCED);
|
|
||||||
}
|
|
||||||
|
|
||||||
ledgerIndex = jsonIndex.asInt();
|
|
||||||
auto ledger = ledgerMaster.getValidatedLedger();
|
|
||||||
|
|
||||||
if (ledgerIndex >= ledger->info().seq)
|
|
||||||
return RPC::make_param_error("Ledger index too large");
|
|
||||||
if (ledgerIndex <= 0)
|
|
||||||
return RPC::make_param_error("Ledger index too small");
|
|
||||||
|
|
||||||
auto const j = context.app.journal("RPCHandler");
|
|
||||||
// Try to get the hash of the desired ledger from the validated
|
|
||||||
// ledger
|
|
||||||
auto neededHash = hashOfSeq(*ledger, ledgerIndex, j);
|
|
||||||
if (!neededHash)
|
|
||||||
{
|
|
||||||
// Find a ledger more likely to have the hash of the desired
|
|
||||||
// ledger
|
|
||||||
auto const refIndex = getCandidateLedger(ledgerIndex);
|
|
||||||
auto refHash = hashOfSeq(*ledger, refIndex, j);
|
|
||||||
XRPL_ASSERT(
|
|
||||||
refHash,
|
|
||||||
"ripple::RPC::getLedgerByContext : nonzero ledger hash");
|
|
||||||
|
|
||||||
ledger = ledgerMaster.getLedgerByHash(*refHash);
|
|
||||||
if (!ledger)
|
|
||||||
{
|
|
||||||
// We don't have the ledger we need to figure out which
|
|
||||||
// ledger they want. Try to get it.
|
|
||||||
|
|
||||||
if (auto il = context.app.getInboundLedgers().acquire(
|
|
||||||
*refHash, refIndex, InboundLedger::Reason::GENERIC))
|
|
||||||
{
|
|
||||||
Json::Value jvResult = RPC::make_error(
|
|
||||||
rpcLGR_NOT_FOUND,
|
|
||||||
"acquiring ledger containing requested index");
|
|
||||||
jvResult[jss::acquiring] =
|
|
||||||
getJson(LedgerFill(*il, &context));
|
|
||||||
return jvResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto il = context.app.getInboundLedgers().find(*refHash))
|
|
||||||
{
|
|
||||||
Json::Value jvResult = RPC::make_error(
|
|
||||||
rpcLGR_NOT_FOUND,
|
|
||||||
"acquiring ledger containing requested index");
|
|
||||||
jvResult[jss::acquiring] = il->getJson(0);
|
|
||||||
return jvResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Likely the app is shutting down
|
|
||||||
return Json::Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
neededHash = hashOfSeq(*ledger, ledgerIndex, j);
|
|
||||||
}
|
|
||||||
XRPL_ASSERT(
|
|
||||||
neededHash,
|
|
||||||
"ripple::RPC::getLedgerByContext : nonzero needed hash");
|
|
||||||
ledgerHash = neededHash ? *neededHash : beast::zero; // kludge
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to get the desired ledger
|
|
||||||
// Verify all nodes even if we think we have it
|
|
||||||
auto ledger = context.app.getInboundLedgers().acquire(
|
|
||||||
ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
|
|
||||||
|
|
||||||
// In standalone mode, accept the ledger from the ledger cache
|
|
||||||
if (!ledger && context.app.config().standalone())
|
|
||||||
ledger = ledgerMaster.getLedgerByHash(ledgerHash);
|
|
||||||
|
|
||||||
if (ledger)
|
|
||||||
return ledger;
|
|
||||||
|
|
||||||
if (auto il = context.app.getInboundLedgers().find(ledgerHash))
|
|
||||||
return il->getJson(0);
|
|
||||||
|
|
||||||
return RPC::make_error(
|
|
||||||
rpcNOT_READY, "findCreate failed to return an inbound ledger");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace RPC
|
} // namespace RPC
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -73,81 +73,6 @@ isRelatedToAccount(
|
|||||||
std::shared_ptr<SLE const> const& sle,
|
std::shared_ptr<SLE const> const& sle,
|
||||||
AccountID const& accountID);
|
AccountID const& accountID);
|
||||||
|
|
||||||
/** Gathers all objects for an account in a ledger.
|
|
||||||
@param ledger Ledger to search account objects.
|
|
||||||
@param account AccountID to find objects for.
|
|
||||||
@param typeFilter Gathers objects of these types. empty gathers all types.
|
|
||||||
@param dirIndex Begin gathering account objects from this directory.
|
|
||||||
@param entryIndex Begin gathering objects from this directory node.
|
|
||||||
@param limit Maximum number of objects to find.
|
|
||||||
@param jvResult A JSON result that holds the request objects.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
getAccountObjects(
|
|
||||||
ReadView const& ledger,
|
|
||||||
AccountID const& account,
|
|
||||||
std::optional<std::vector<LedgerEntryType>> const& typeFilter,
|
|
||||||
uint256 dirIndex,
|
|
||||||
uint256 entryIndex,
|
|
||||||
std::uint32_t const limit,
|
|
||||||
Json::Value& jvResult);
|
|
||||||
|
|
||||||
/** Get ledger by hash
|
|
||||||
If there is no error in the return value, the ledger pointer will have
|
|
||||||
been filled
|
|
||||||
*/
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
getLedger(T& ledger, uint256 const& ledgerHash, Context& context);
|
|
||||||
|
|
||||||
/** Get ledger by sequence
|
|
||||||
If there is no error in the return value, the ledger pointer will have
|
|
||||||
been filled
|
|
||||||
*/
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
getLedger(T& ledger, uint32_t ledgerIndex, Context& context);
|
|
||||||
|
|
||||||
enum LedgerShortcut { CURRENT, CLOSED, VALIDATED };
|
|
||||||
/** Get ledger specified in shortcut.
|
|
||||||
If there is no error in the return value, the ledger pointer will have
|
|
||||||
been filled
|
|
||||||
*/
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
getLedger(T& ledger, LedgerShortcut shortcut, Context& context);
|
|
||||||
|
|
||||||
/** Look up a ledger from a request and fill a Json::Result with either
|
|
||||||
an error, or data representing a ledger.
|
|
||||||
|
|
||||||
If there is no error in the return value, then the ledger pointer will have
|
|
||||||
been filled.
|
|
||||||
*/
|
|
||||||
Json::Value
|
|
||||||
lookupLedger(std::shared_ptr<ReadView const>&, JsonContext&);
|
|
||||||
|
|
||||||
/** Look up a ledger from a request and fill a Json::Result with the data
|
|
||||||
representing a ledger.
|
|
||||||
|
|
||||||
If the returned Status is OK, the ledger pointer will have been filled.
|
|
||||||
*/
|
|
||||||
Status
|
|
||||||
lookupLedger(
|
|
||||||
std::shared_ptr<ReadView const>&,
|
|
||||||
JsonContext&,
|
|
||||||
Json::Value& result);
|
|
||||||
|
|
||||||
template <class T, class R>
|
|
||||||
Status
|
|
||||||
ledgerFromRequest(T& ledger, GRPCContext<R>& context);
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
Status
|
|
||||||
ledgerFromSpecifier(
|
|
||||||
T& ledger,
|
|
||||||
org::xrpl::rpc::v1::LedgerSpecifier const& specifier,
|
|
||||||
Context& context);
|
|
||||||
|
|
||||||
hash_set<AccountID>
|
hash_set<AccountID>
|
||||||
parseAccountIds(Json::Value const& jvArray);
|
parseAccountIds(Json::Value const& jvArray);
|
||||||
|
|
||||||
@@ -194,11 +119,6 @@ chooseLedgerEntryType(Json::Value const& params);
|
|||||||
bool
|
bool
|
||||||
isAccountObjectsValidType(LedgerEntryType const& type);
|
isAccountObjectsValidType(LedgerEntryType const& type);
|
||||||
|
|
||||||
/** Return a ledger based on ledger_hash or ledger_index,
|
|
||||||
or an RPC error */
|
|
||||||
std::variant<std::shared_ptr<Ledger const>, Json::Value>
|
|
||||||
getLedgerByContext(RPC::JsonContext& context);
|
|
||||||
|
|
||||||
std::optional<std::pair<PublicKey, SecretKey>>
|
std::optional<std::pair<PublicKey, SecretKey>>
|
||||||
keypairForSignature(
|
keypairForSignature(
|
||||||
Json::Value const& params,
|
Json::Value const& params,
|
||||||
|
|||||||
458
src/xrpld/rpc/detail/RPCLedgerHelpers.cpp
Normal file
458
src/xrpld/rpc/detail/RPCLedgerHelpers.cpp
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||||
|
#include <xrpld/app/ledger/LedgerToJson.h>
|
||||||
|
#include <xrpld/app/ledger/OpenLedger.h>
|
||||||
|
#include <xrpld/app/misc/Transaction.h>
|
||||||
|
#include <xrpld/app/paths/TrustLine.h>
|
||||||
|
#include <xrpld/app/rdb/RelationalDatabase.h>
|
||||||
|
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||||
|
#include <xrpld/rpc/Context.h>
|
||||||
|
#include <xrpld/rpc/DeliveredAmount.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
|
||||||
|
#include <xrpl/ledger/View.h>
|
||||||
|
#include <xrpl/protocol/AccountID.h>
|
||||||
|
#include <xrpl/protocol/RPCErr.h>
|
||||||
|
#include <xrpl/protocol/nftPageMask.h>
|
||||||
|
#include <xrpl/resource/Fees.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace RPC {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool
|
||||||
|
isValidatedOld(LedgerMaster& ledgerMaster, bool standalone)
|
||||||
|
{
|
||||||
|
if (standalone)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ledgerMaster.getValidatedLedgerAge() > Tuning::maxValidatedLedgerAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
ledgerFromRequest(T& ledger, JsonContext& context)
|
||||||
|
{
|
||||||
|
ledger.reset();
|
||||||
|
|
||||||
|
auto& params = context.params;
|
||||||
|
|
||||||
|
auto indexValue = params[jss::ledger_index];
|
||||||
|
auto hashValue = params[jss::ledger_hash];
|
||||||
|
|
||||||
|
// We need to support the legacy "ledger" field.
|
||||||
|
auto& legacyLedger = params[jss::ledger];
|
||||||
|
if (legacyLedger)
|
||||||
|
{
|
||||||
|
if (legacyLedger.asString().size() > 12)
|
||||||
|
hashValue = legacyLedger;
|
||||||
|
else
|
||||||
|
indexValue = legacyLedger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hashValue.isNull())
|
||||||
|
{
|
||||||
|
if (!hashValue.isString())
|
||||||
|
return {rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||||
|
|
||||||
|
uint256 ledgerHash;
|
||||||
|
if (!ledgerHash.parseHex(hashValue.asString()))
|
||||||
|
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||||
|
return getLedger(ledger, ledgerHash, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!indexValue.isConvertibleTo(Json::stringValue))
|
||||||
|
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||||
|
|
||||||
|
auto const index = indexValue.asString();
|
||||||
|
|
||||||
|
if (index == "current" || index.empty())
|
||||||
|
return getLedger(ledger, LedgerShortcut::CURRENT, context);
|
||||||
|
|
||||||
|
if (index == "validated")
|
||||||
|
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
|
||||||
|
|
||||||
|
if (index == "closed")
|
||||||
|
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
||||||
|
|
||||||
|
std::uint32_t val;
|
||||||
|
if (!beast::lexicalCastChecked(val, index))
|
||||||
|
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||||
|
|
||||||
|
return getLedger(ledger, val, context);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
template <class T, class R>
|
||||||
|
Status
|
||||||
|
ledgerFromRequest(T& ledger, GRPCContext<R>& context)
|
||||||
|
{
|
||||||
|
R& request = context.params;
|
||||||
|
return ledgerFromSpecifier(ledger, request.ledger(), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicit instantiation of above function
|
||||||
|
template Status
|
||||||
|
ledgerFromRequest<>(
|
||||||
|
std::shared_ptr<ReadView const>&,
|
||||||
|
GRPCContext<org::xrpl::rpc::v1::GetLedgerEntryRequest>&);
|
||||||
|
|
||||||
|
// explicit instantiation of above function
|
||||||
|
template Status
|
||||||
|
ledgerFromRequest<>(
|
||||||
|
std::shared_ptr<ReadView const>&,
|
||||||
|
GRPCContext<org::xrpl::rpc::v1::GetLedgerDataRequest>&);
|
||||||
|
|
||||||
|
// explicit instantiation of above function
|
||||||
|
template Status
|
||||||
|
ledgerFromRequest<>(
|
||||||
|
std::shared_ptr<ReadView const>&,
|
||||||
|
GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest>&);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
ledgerFromSpecifier(
|
||||||
|
T& ledger,
|
||||||
|
org::xrpl::rpc::v1::LedgerSpecifier const& specifier,
|
||||||
|
Context& context)
|
||||||
|
{
|
||||||
|
ledger.reset();
|
||||||
|
|
||||||
|
using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
|
||||||
|
LedgerCase ledgerCase = specifier.ledger_case();
|
||||||
|
switch (ledgerCase)
|
||||||
|
{
|
||||||
|
case LedgerCase::kHash: {
|
||||||
|
if (auto hash = uint256::fromVoidChecked(specifier.hash()))
|
||||||
|
{
|
||||||
|
return getLedger(ledger, *hash, context);
|
||||||
|
}
|
||||||
|
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||||
|
}
|
||||||
|
case LedgerCase::kSequence:
|
||||||
|
return getLedger(ledger, specifier.sequence(), context);
|
||||||
|
case LedgerCase::kShortcut:
|
||||||
|
[[fallthrough]];
|
||||||
|
case LedgerCase::LEDGER_NOT_SET: {
|
||||||
|
auto const shortcut = specifier.shortcut();
|
||||||
|
if (shortcut ==
|
||||||
|
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
|
||||||
|
{
|
||||||
|
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (shortcut ==
|
||||||
|
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT ||
|
||||||
|
shortcut ==
|
||||||
|
org::xrpl::rpc::v1::LedgerSpecifier::
|
||||||
|
SHORTCUT_UNSPECIFIED)
|
||||||
|
{
|
||||||
|
return getLedger(ledger, LedgerShortcut::CURRENT, context);
|
||||||
|
}
|
||||||
|
else if (
|
||||||
|
shortcut ==
|
||||||
|
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
|
||||||
|
{
|
||||||
|
return getLedger(ledger, LedgerShortcut::CLOSED, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
getLedger(T& ledger, uint256 const& ledgerHash, Context& context)
|
||||||
|
{
|
||||||
|
ledger = context.ledgerMaster.getLedgerByHash(ledgerHash);
|
||||||
|
if (ledger == nullptr)
|
||||||
|
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
getLedger(T& ledger, uint32_t ledgerIndex, Context& context)
|
||||||
|
{
|
||||||
|
ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex);
|
||||||
|
if (ledger == nullptr)
|
||||||
|
{
|
||||||
|
auto cur = context.ledgerMaster.getCurrentLedger();
|
||||||
|
if (cur->info().seq == ledgerIndex)
|
||||||
|
{
|
||||||
|
ledger = cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ledger == nullptr)
|
||||||
|
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||||
|
|
||||||
|
if (ledger->info().seq > context.ledgerMaster.getValidLedgerIndex() &&
|
||||||
|
isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
|
||||||
|
{
|
||||||
|
ledger.reset();
|
||||||
|
if (context.apiVersion == 1)
|
||||||
|
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||||
|
return {rpcNOT_SYNCED, "notSynced"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
getLedger(T& ledger, LedgerShortcut shortcut, Context& context)
|
||||||
|
{
|
||||||
|
if (isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
|
||||||
|
{
|
||||||
|
if (context.apiVersion == 1)
|
||||||
|
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||||
|
return {rpcNOT_SYNCED, "notSynced"};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shortcut == LedgerShortcut::VALIDATED)
|
||||||
|
{
|
||||||
|
ledger = context.ledgerMaster.getValidatedLedger();
|
||||||
|
if (ledger == nullptr)
|
||||||
|
{
|
||||||
|
if (context.apiVersion == 1)
|
||||||
|
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||||
|
return {rpcNOT_SYNCED, "notSynced"};
|
||||||
|
}
|
||||||
|
|
||||||
|
XRPL_ASSERT(
|
||||||
|
!ledger->open(), "ripple::RPC::getLedger : validated is not open");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (shortcut == LedgerShortcut::CURRENT)
|
||||||
|
{
|
||||||
|
ledger = context.ledgerMaster.getCurrentLedger();
|
||||||
|
XRPL_ASSERT(
|
||||||
|
ledger->open(), "ripple::RPC::getLedger : current is open");
|
||||||
|
}
|
||||||
|
else if (shortcut == LedgerShortcut::CLOSED)
|
||||||
|
{
|
||||||
|
ledger = context.ledgerMaster.getClosedLedger();
|
||||||
|
XRPL_ASSERT(
|
||||||
|
!ledger->open(), "ripple::RPC::getLedger : closed is not open");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ledger == nullptr)
|
||||||
|
{
|
||||||
|
if (context.apiVersion == 1)
|
||||||
|
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||||
|
return {rpcNOT_SYNCED, "notSynced"};
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto const minSequenceGap = 10;
|
||||||
|
|
||||||
|
if (ledger->info().seq + minSequenceGap <
|
||||||
|
context.ledgerMaster.getValidLedgerIndex())
|
||||||
|
{
|
||||||
|
ledger.reset();
|
||||||
|
if (context.apiVersion == 1)
|
||||||
|
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
|
||||||
|
return {rpcNOT_SYNCED, "notSynced"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit instantiation of above three functions
|
||||||
|
template Status
|
||||||
|
getLedger<>(std::shared_ptr<ReadView const>&, uint32_t, Context&);
|
||||||
|
|
||||||
|
template Status
|
||||||
|
getLedger<>(
|
||||||
|
std::shared_ptr<ReadView const>&,
|
||||||
|
LedgerShortcut shortcut,
|
||||||
|
Context&);
|
||||||
|
|
||||||
|
template Status
|
||||||
|
getLedger<>(std::shared_ptr<ReadView const>&, uint256 const&, Context&);
|
||||||
|
|
||||||
|
// The previous version of the lookupLedger command would accept the
|
||||||
|
// "ledger_index" argument as a string and silently treat it as a request to
|
||||||
|
// return the current ledger which, while not strictly wrong, could cause a lot
|
||||||
|
// of confusion.
|
||||||
|
//
|
||||||
|
// The code now robustly validates the input and ensures that the only possible
|
||||||
|
// values for the "ledger_index" parameter are the index of a ledger passed as
|
||||||
|
// an integer or one of the strings "current", "closed" or "validated".
|
||||||
|
// Additionally, the code ensures that the value passed in "ledger_hash" is a
|
||||||
|
// string and a valid hash. Invalid values will return an appropriate error
|
||||||
|
// code.
|
||||||
|
//
|
||||||
|
// In the absence of the "ledger_hash" or "ledger_index" parameters, the code
|
||||||
|
// assumes that "ledger_index" has the value "current".
|
||||||
|
//
|
||||||
|
// Returns a Json::objectValue. If there was an error, it will be in that
|
||||||
|
// return value. Otherwise, the object contains the field "validated" and
|
||||||
|
// optionally the fields "ledger_hash", "ledger_index" and
|
||||||
|
// "ledger_current_index", if they are defined.
|
||||||
|
Status
|
||||||
|
lookupLedger(
|
||||||
|
std::shared_ptr<ReadView const>& ledger,
|
||||||
|
JsonContext& context,
|
||||||
|
Json::Value& result)
|
||||||
|
{
|
||||||
|
if (auto status = ledgerFromRequest(ledger, context))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
auto& info = ledger->info();
|
||||||
|
|
||||||
|
if (!ledger->open())
|
||||||
|
{
|
||||||
|
result[jss::ledger_hash] = to_string(info.hash);
|
||||||
|
result[jss::ledger_index] = info.seq;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result[jss::ledger_current_index] = info.seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[jss::validated] = context.ledgerMaster.isValidated(*ledger);
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value
|
||||||
|
lookupLedger(std::shared_ptr<ReadView const>& ledger, JsonContext& context)
|
||||||
|
{
|
||||||
|
Json::Value result;
|
||||||
|
if (auto status = lookupLedger(ledger, context, result))
|
||||||
|
status.inject(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<std::shared_ptr<Ledger const>, Json::Value>
|
||||||
|
getLedgerByContext(RPC::JsonContext& context)
|
||||||
|
{
|
||||||
|
auto const hasHash = context.params.isMember(jss::ledger_hash);
|
||||||
|
auto const hasIndex = context.params.isMember(jss::ledger_index);
|
||||||
|
std::uint32_t ledgerIndex = 0;
|
||||||
|
|
||||||
|
auto& ledgerMaster = context.app.getLedgerMaster();
|
||||||
|
LedgerHash ledgerHash;
|
||||||
|
|
||||||
|
if ((hasHash && hasIndex) || !(hasHash || hasIndex))
|
||||||
|
{
|
||||||
|
return RPC::make_param_error(
|
||||||
|
"Exactly one of ledger_hash and ledger_index can be set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.loadType = Resource::feeHeavyBurdenRPC;
|
||||||
|
|
||||||
|
if (hasHash)
|
||||||
|
{
|
||||||
|
auto const& jsonHash = context.params[jss::ledger_hash];
|
||||||
|
if (!jsonHash.isString() || !ledgerHash.parseHex(jsonHash.asString()))
|
||||||
|
return RPC::invalid_field_error(jss::ledger_hash);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto const& jsonIndex = context.params[jss::ledger_index];
|
||||||
|
if (!jsonIndex.isInt())
|
||||||
|
return RPC::invalid_field_error(jss::ledger_index);
|
||||||
|
|
||||||
|
// We need a validated ledger to get the hash from the sequence
|
||||||
|
if (ledgerMaster.getValidatedLedgerAge() >
|
||||||
|
RPC::Tuning::maxValidatedLedgerAge)
|
||||||
|
{
|
||||||
|
if (context.apiVersion == 1)
|
||||||
|
return rpcError(rpcNO_CURRENT);
|
||||||
|
return rpcError(rpcNOT_SYNCED);
|
||||||
|
}
|
||||||
|
|
||||||
|
ledgerIndex = jsonIndex.asInt();
|
||||||
|
auto ledger = ledgerMaster.getValidatedLedger();
|
||||||
|
|
||||||
|
if (ledgerIndex >= ledger->info().seq)
|
||||||
|
return RPC::make_param_error("Ledger index too large");
|
||||||
|
if (ledgerIndex <= 0)
|
||||||
|
return RPC::make_param_error("Ledger index too small");
|
||||||
|
|
||||||
|
auto const j = context.app.journal("RPCHandler");
|
||||||
|
// Try to get the hash of the desired ledger from the validated
|
||||||
|
// ledger
|
||||||
|
auto neededHash = hashOfSeq(*ledger, ledgerIndex, j);
|
||||||
|
if (!neededHash)
|
||||||
|
{
|
||||||
|
// Find a ledger more likely to have the hash of the desired
|
||||||
|
// ledger
|
||||||
|
auto const refIndex = getCandidateLedger(ledgerIndex);
|
||||||
|
auto refHash = hashOfSeq(*ledger, refIndex, j);
|
||||||
|
XRPL_ASSERT(
|
||||||
|
refHash,
|
||||||
|
"ripple::RPC::getLedgerByContext : nonzero ledger hash");
|
||||||
|
|
||||||
|
ledger = ledgerMaster.getLedgerByHash(*refHash);
|
||||||
|
if (!ledger)
|
||||||
|
{
|
||||||
|
// We don't have the ledger we need to figure out which
|
||||||
|
// ledger they want. Try to get it.
|
||||||
|
|
||||||
|
if (auto il = context.app.getInboundLedgers().acquire(
|
||||||
|
*refHash, refIndex, InboundLedger::Reason::GENERIC))
|
||||||
|
{
|
||||||
|
Json::Value jvResult = RPC::make_error(
|
||||||
|
rpcLGR_NOT_FOUND,
|
||||||
|
"acquiring ledger containing requested index");
|
||||||
|
jvResult[jss::acquiring] =
|
||||||
|
getJson(LedgerFill(*il, &context));
|
||||||
|
return jvResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto il = context.app.getInboundLedgers().find(*refHash))
|
||||||
|
{
|
||||||
|
Json::Value jvResult = RPC::make_error(
|
||||||
|
rpcLGR_NOT_FOUND,
|
||||||
|
"acquiring ledger containing requested index");
|
||||||
|
jvResult[jss::acquiring] = il->getJson(0);
|
||||||
|
return jvResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Likely the app is shutting down
|
||||||
|
return Json::Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
neededHash = hashOfSeq(*ledger, ledgerIndex, j);
|
||||||
|
}
|
||||||
|
XRPL_ASSERT(
|
||||||
|
neededHash,
|
||||||
|
"ripple::RPC::getLedgerByContext : nonzero needed hash");
|
||||||
|
ledgerHash = neededHash ? *neededHash : beast::zero; // kludge
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get the desired ledger
|
||||||
|
// Verify all nodes even if we think we have it
|
||||||
|
auto ledger = context.app.getInboundLedgers().acquire(
|
||||||
|
ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
|
||||||
|
|
||||||
|
// In standalone mode, accept the ledger from the ledger cache
|
||||||
|
if (!ledger && context.app.config().standalone())
|
||||||
|
ledger = ledgerMaster.getLedgerByHash(ledgerHash);
|
||||||
|
|
||||||
|
if (ledger)
|
||||||
|
return ledger;
|
||||||
|
|
||||||
|
if (auto il = context.app.getInboundLedgers().find(ledgerHash))
|
||||||
|
return il->getJson(0);
|
||||||
|
|
||||||
|
return RPC::make_error(
|
||||||
|
rpcNOT_READY, "findCreate failed to return an inbound ledger");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
|
} // namespace ripple
|
||||||
96
src/xrpld/rpc/detail/RPCLedgerHelpers.h
Normal file
96
src/xrpld/rpc/detail/RPCLedgerHelpers.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#ifndef XRPL_RPC_RPCLEDGERHELPERS_H_INCLUDED
|
||||||
|
#define XRPL_RPC_RPCLEDGERHELPERS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <xrpld/app/misc/NetworkOPs.h>
|
||||||
|
#include <xrpld/app/misc/TxQ.h>
|
||||||
|
#include <xrpld/rpc/Context.h>
|
||||||
|
#include <xrpld/rpc/Status.h>
|
||||||
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
|
#include <xrpl/beast/core/SemanticVersion.h>
|
||||||
|
#include <xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.pb.h>
|
||||||
|
#include <xrpl/protocol/ApiVersion.h>
|
||||||
|
#include <xrpl/protocol/SecretKey.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace Json {
|
||||||
|
class Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
class ReadView;
|
||||||
|
class Transaction;
|
||||||
|
|
||||||
|
namespace RPC {
|
||||||
|
|
||||||
|
struct JsonContext;
|
||||||
|
|
||||||
|
/** Get ledger by hash
|
||||||
|
If there is no error in the return value, the ledger pointer will have
|
||||||
|
been filled
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
getLedger(T& ledger, uint256 const& ledgerHash, Context& context);
|
||||||
|
|
||||||
|
/** Get ledger by sequence
|
||||||
|
If there is no error in the return value, the ledger pointer will have
|
||||||
|
been filled
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
getLedger(T& ledger, uint32_t ledgerIndex, Context& context);
|
||||||
|
|
||||||
|
enum LedgerShortcut { CURRENT, CLOSED, VALIDATED };
|
||||||
|
/** Get ledger specified in shortcut.
|
||||||
|
If there is no error in the return value, the ledger pointer will have
|
||||||
|
been filled
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
getLedger(T& ledger, LedgerShortcut shortcut, Context& context);
|
||||||
|
|
||||||
|
/** Look up a ledger from a request and fill a Json::Result with either
|
||||||
|
an error, or data representing a ledger.
|
||||||
|
|
||||||
|
If there is no error in the return value, then the ledger pointer will have
|
||||||
|
been filled.
|
||||||
|
*/
|
||||||
|
Json::Value
|
||||||
|
lookupLedger(std::shared_ptr<ReadView const>&, JsonContext&);
|
||||||
|
|
||||||
|
/** Look up a ledger from a request and fill a Json::Result with the data
|
||||||
|
representing a ledger.
|
||||||
|
|
||||||
|
If the returned Status is OK, the ledger pointer will have been filled.
|
||||||
|
*/
|
||||||
|
Status
|
||||||
|
lookupLedger(
|
||||||
|
std::shared_ptr<ReadView const>&,
|
||||||
|
JsonContext&,
|
||||||
|
Json::Value& result);
|
||||||
|
|
||||||
|
template <class T, class R>
|
||||||
|
Status
|
||||||
|
ledgerFromRequest(T& ledger, GRPCContext<R>& context);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Status
|
||||||
|
ledgerFromSpecifier(
|
||||||
|
T& ledger,
|
||||||
|
org::xrpl::rpc::v1::LedgerSpecifier const& specifier,
|
||||||
|
Context& context);
|
||||||
|
|
||||||
|
/** Return a ledger based on ledger_hash or ledger_index,
|
||||||
|
or an RPC error */
|
||||||
|
std::variant<std::shared_ptr<Ledger const>, Json::Value>
|
||||||
|
getLedgerByContext(RPC::JsonContext& context);
|
||||||
|
|
||||||
|
} // namespace RPC
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <xrpld/app/misc/AMMUtils.h>
|
#include <xrpld/app/misc/AMMUtils.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/detail/Tuning.h>
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <xrpld/app/paths/TrustLine.h>
|
#include <xrpld/app/paths/TrustLine.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/protocol/ErrorCodes.h>
|
#include <xrpl/protocol/ErrorCodes.h>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/GRPCHandlers.h>
|
#include <xrpld/rpc/GRPCHandlers.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <xrpld/app/paths/TrustLine.h>
|
#include <xrpld/app/paths/TrustLine.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/detail/Tuning.h>
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/detail/Tuning.h>
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
@@ -158,6 +159,198 @@ doAccountNFTs(RPC::JsonContext& context)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
getAccountObjects(
|
||||||
|
ReadView const& ledger,
|
||||||
|
AccountID const& account,
|
||||||
|
std::optional<std::vector<LedgerEntryType>> const& typeFilter,
|
||||||
|
uint256 dirIndex,
|
||||||
|
uint256 entryIndex,
|
||||||
|
std::uint32_t const limit,
|
||||||
|
Json::Value& jvResult)
|
||||||
|
{
|
||||||
|
// check if dirIndex is valid
|
||||||
|
if (!dirIndex.isZero() && !ledger.read({ltDIR_NODE, dirIndex}))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto typeMatchesFilter = [](std::vector<LedgerEntryType> const& typeFilter,
|
||||||
|
LedgerEntryType ledgerType) {
|
||||||
|
auto it = std::find(typeFilter.begin(), typeFilter.end(), ledgerType);
|
||||||
|
return it != typeFilter.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
// if dirIndex != 0, then all NFTs have already been returned. only
|
||||||
|
// iterate NFT pages if the filter says so AND dirIndex == 0
|
||||||
|
bool iterateNFTPages =
|
||||||
|
(!typeFilter.has_value() ||
|
||||||
|
typeMatchesFilter(typeFilter.value(), ltNFTOKEN_PAGE)) &&
|
||||||
|
dirIndex == beast::zero;
|
||||||
|
|
||||||
|
Keylet const firstNFTPage = keylet::nftpage_min(account);
|
||||||
|
|
||||||
|
// we need to check the marker to see if it is an NFTTokenPage index.
|
||||||
|
if (iterateNFTPages && entryIndex != beast::zero)
|
||||||
|
{
|
||||||
|
// if it is we will try to iterate the pages up to the limit
|
||||||
|
// and then change over to the owner directory
|
||||||
|
|
||||||
|
if (firstNFTPage.key != (entryIndex & ~nft::pageMask))
|
||||||
|
iterateNFTPages = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue);
|
||||||
|
|
||||||
|
// this is a mutable version of limit, used to seamlessly switch
|
||||||
|
// to iterating directory entries when nftokenpages are exhausted
|
||||||
|
uint32_t mlimit = limit;
|
||||||
|
|
||||||
|
// iterate NFTokenPages preferentially
|
||||||
|
if (iterateNFTPages)
|
||||||
|
{
|
||||||
|
Keylet const first = entryIndex == beast::zero
|
||||||
|
? firstNFTPage
|
||||||
|
: Keylet{ltNFTOKEN_PAGE, entryIndex};
|
||||||
|
|
||||||
|
Keylet const last = keylet::nftpage_max(account);
|
||||||
|
|
||||||
|
// current key
|
||||||
|
uint256 ck = ledger.succ(first.key, last.key.next()).value_or(last.key);
|
||||||
|
|
||||||
|
// current page
|
||||||
|
auto cp = ledger.read(Keylet{ltNFTOKEN_PAGE, ck});
|
||||||
|
|
||||||
|
while (cp)
|
||||||
|
{
|
||||||
|
jvObjects.append(cp->getJson(JsonOptions::none));
|
||||||
|
auto const npm = (*cp)[~sfNextPageMin];
|
||||||
|
if (npm)
|
||||||
|
cp = ledger.read(Keylet(ltNFTOKEN_PAGE, *npm));
|
||||||
|
else
|
||||||
|
cp = nullptr;
|
||||||
|
|
||||||
|
if (--mlimit == 0)
|
||||||
|
{
|
||||||
|
if (cp)
|
||||||
|
{
|
||||||
|
jvResult[jss::limit] = limit;
|
||||||
|
jvResult[jss::marker] = std::string("0,") + to_string(ck);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!npm)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ck = *npm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if execution reaches here then we're about to transition
|
||||||
|
// to iterating the root directory (and the conventional
|
||||||
|
// behaviour of this RPC function.) Therefore we should
|
||||||
|
// zero entryIndex so as not to terribly confuse things.
|
||||||
|
entryIndex = beast::zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const root = keylet::ownerDir(account);
|
||||||
|
auto found = false;
|
||||||
|
|
||||||
|
if (dirIndex.isZero())
|
||||||
|
{
|
||||||
|
dirIndex = root.key;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = ledger.read({ltDIR_NODE, dirIndex});
|
||||||
|
if (!dir)
|
||||||
|
{
|
||||||
|
// it's possible the user had nftoken pages but no
|
||||||
|
// directory entries. If there's no nftoken page, we will
|
||||||
|
// give empty array for account_objects.
|
||||||
|
if (mlimit >= limit)
|
||||||
|
jvResult[jss::account_objects] = Json::arrayValue;
|
||||||
|
|
||||||
|
// non-zero dirIndex validity was checked in the beginning of this
|
||||||
|
// function; by this point, it should be zero. This function returns
|
||||||
|
// true regardless of nftoken page presence; if absent, account_objects
|
||||||
|
// is already set as an empty array. Notice we will only return false in
|
||||||
|
// this function when entryIndex can not be found, indicating an invalid
|
||||||
|
// marker error.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t i = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
auto const& entries = dir->getFieldV256(sfIndexes);
|
||||||
|
auto iter = entries.begin();
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
iter = std::find(iter, entries.end(), entryIndex);
|
||||||
|
if (iter == entries.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's possible that the returned NFTPages exactly filled the
|
||||||
|
// response. Check for that condition.
|
||||||
|
if (i == mlimit && mlimit < limit)
|
||||||
|
{
|
||||||
|
jvResult[jss::limit] = limit;
|
||||||
|
jvResult[jss::marker] =
|
||||||
|
to_string(dirIndex) + ',' + to_string(*iter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; iter != entries.end(); ++iter)
|
||||||
|
{
|
||||||
|
auto const sleNode = ledger.read(keylet::child(*iter));
|
||||||
|
|
||||||
|
if (!typeFilter.has_value() ||
|
||||||
|
typeMatchesFilter(typeFilter.value(), sleNode->getType()))
|
||||||
|
{
|
||||||
|
jvObjects.append(sleNode->getJson(JsonOptions::none));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++i == mlimit)
|
||||||
|
{
|
||||||
|
if (++iter != entries.end())
|
||||||
|
{
|
||||||
|
jvResult[jss::limit] = limit;
|
||||||
|
jvResult[jss::marker] =
|
||||||
|
to_string(dirIndex) + ',' + to_string(*iter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const nodeIndex = dir->getFieldU64(sfIndexNext);
|
||||||
|
if (nodeIndex == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
dirIndex = keylet::page(root, nodeIndex).key;
|
||||||
|
dir = ledger.read({ltDIR_NODE, dirIndex});
|
||||||
|
if (!dir)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (i == mlimit)
|
||||||
|
{
|
||||||
|
auto const& e = dir->getFieldV256(sfIndexes);
|
||||||
|
if (!e.empty())
|
||||||
|
{
|
||||||
|
jvResult[jss::limit] = limit;
|
||||||
|
jvResult[jss::marker] =
|
||||||
|
to_string(dirIndex) + ',' + to_string(*e.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Json::Value
|
Json::Value
|
||||||
doAccountObjects(RPC::JsonContext& context)
|
doAccountObjects(RPC::JsonContext& context)
|
||||||
{
|
{
|
||||||
@@ -265,7 +458,7 @@ doAccountObjects(RPC::JsonContext& context)
|
|||||||
return RPC::invalid_field_error(jss::marker);
|
return RPC::invalid_field_error(jss::marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!RPC::getAccountObjects(
|
if (!getAccountObjects(
|
||||||
*ledger,
|
*ledger,
|
||||||
accountID,
|
accountID,
|
||||||
typeFilter,
|
typeFilter,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/detail/Tuning.h>
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <xrpld/rpc/BookChanges.h>
|
#include <xrpld/rpc/BookChanges.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/basics/Log.h>
|
#include <xrpl/basics/Log.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/CredentialHelpers.h>
|
#include <xrpl/ledger/CredentialHelpers.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <xrpld/app/main/Application.h>
|
#include <xrpld/app/main/Application.h>
|
||||||
#include <xrpld/app/paths/TrustLine.h>
|
#include <xrpld/app/paths/TrustLine.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/protocol/AccountID.h>
|
#include <xrpl/protocol/AccountID.h>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||||
#include <xrpld/app/main/Application.h>
|
#include <xrpld/app/main/Application.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <xrpld/rpc/GRPCHandlers.h>
|
#include <xrpld/rpc/GRPCHandlers.h>
|
||||||
#include <xrpld/rpc/Role.h>
|
#include <xrpld/rpc/Role.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/detail/Tuning.h>
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include <xrpld/rpc/GRPCHandlers.h>
|
#include <xrpld/rpc/GRPCHandlers.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
std::pair<org::xrpl::rpc::v1::GetLedgerDiffResponse, grpc::Status>
|
std::pair<org::xrpl::rpc::v1::GetLedgerDiffResponse, grpc::Status>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/GRPCHandlers.h>
|
#include <xrpld/rpc/GRPCHandlers.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/handlers/LedgerEntryHelpers.h>
|
#include <xrpld/rpc/handlers/LedgerEntryHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/basics/StringUtilities.h>
|
#include <xrpl/basics/StringUtilities.h>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <xrpld/app/misc/LoadFeeTrack.h>
|
#include <xrpld/app/misc/LoadFeeTrack.h>
|
||||||
#include <xrpld/rpc/GRPCHandlers.h>
|
#include <xrpld/rpc/GRPCHandlers.h>
|
||||||
#include <xrpld/rpc/Role.h>
|
#include <xrpld/rpc/Role.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/handlers/LedgerHandler.h>
|
#include <xrpld/rpc/handlers/LedgerHandler.h>
|
||||||
|
|
||||||
#include <xrpl/protocol/ErrorCodes.h>
|
#include <xrpl/protocol/ErrorCodes.h>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include <xrpld/app/ledger/LedgerToJson.h>
|
#include <xrpld/app/ledger/LedgerToJson.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/basics/strHex.h>
|
#include <xrpl/basics/strHex.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <xrpld/app/ledger/LedgerToJson.h>
|
#include <xrpld/app/ledger/LedgerToJson.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/protocol/ErrorCodes.h>
|
#include <xrpl/protocol/ErrorCodes.h>
|
||||||
#include <xrpl/protocol/jss.h>
|
#include <xrpl/protocol/jss.h>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/detail/Tuning.h>
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <xrpld/app/paths/TrustLine.h>
|
#include <xrpld/app/paths/TrustLine.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCHelpers.h>
|
||||||
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
#include <xrpld/rpc/detail/Tuning.h>
|
#include <xrpld/rpc/detail/Tuning.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include <xrpld/app/paths/PathRequests.h>
|
#include <xrpld/app/paths/PathRequests.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/LegacyPathFind.h>
|
#include <xrpld/rpc/detail/LegacyPathFind.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/protocol/RPCErr.h>
|
#include <xrpl/protocol/RPCErr.h>
|
||||||
#include <xrpl/resource/Fees.h>
|
#include <xrpl/resource/Fees.h>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||||
#include <xrpld/app/misc/DeliverMax.h>
|
#include <xrpld/app/misc/DeliverMax.h>
|
||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/protocol/jss.h>
|
#include <xrpl/protocol/jss.h>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include <xrpld/rpc/Context.h>
|
#include <xrpld/rpc/Context.h>
|
||||||
#include <xrpld/rpc/detail/RPCHelpers.h>
|
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||||
|
|
||||||
#include <xrpl/beast/utility/Zero.h>
|
#include <xrpl/beast/utility/Zero.h>
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user