mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
There were situations where `marker`s returned by `account_lines` did
not work on subsequent requests, returning "Invalid Parameters".
This was caused by the optimization implemented in "Enforce account RPC
limits by account objects traversed":
e28989638d
Previously, the ledger traversal would find up to `limit` account lines,
and if there were more, the marker would be derived from the key of the
next account line. After the change, ledger traversal would _consider_
up to `limit` account objects of any kind found in the account's
directory structure. If there were more, the marker would be derived
from the key of the next object, regardless of type.
With this optimization, it is expected that `account_lines` may return
fewer than `limit` account lines - even 0 - along with a marker
indicating that there are may be more available.
The problem is that this optimization did not update the
`RPC::isOwnedByAccount` helper function to handle those other object
types. Additionally, XLS-20 added `ltNFTOKEN_OFFER` ledger objects to
objects that have been added to the account's directory structure, but
did not update `RPC::isOwnedByAccount` to be able to handle those
objects. The `marker` provided in the example for #4354 includes the key
for an `ltNFTOKEN_OFFER`. When that `marker` is used on subsequent
calls, it is not recognized as valid, and so the request fails.
* Add unit test that walks all the object types and verifies that all of
their indexes can work as a marker.
* Fix #4340
* Fix #4354
300 lines
9.1 KiB
C++
300 lines
9.1 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012=2014 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef RIPPLE_RPC_RPCHELPERS_H_INCLUDED
|
|
#define RIPPLE_RPC_RPCHELPERS_H_INCLUDED
|
|
|
|
#include <ripple/beast/core/SemanticVersion.h>
|
|
#include <ripple/protocol/TxMeta.h>
|
|
|
|
#include <ripple/app/misc/NetworkOPs.h>
|
|
#include <ripple/app/misc/TxQ.h>
|
|
#include <ripple/protocol/SecretKey.h>
|
|
#include <ripple/rpc/Context.h>
|
|
#include <ripple/rpc/Status.h>
|
|
#include <ripple/rpc/impl/Tuning.h>
|
|
#include <optional>
|
|
#include <org/xrpl/rpc/v1/xrp_ledger.pb.h>
|
|
#include <variant>
|
|
|
|
namespace Json {
|
|
class Value;
|
|
}
|
|
|
|
namespace ripple {
|
|
|
|
class ReadView;
|
|
class Transaction;
|
|
|
|
namespace RPC {
|
|
|
|
struct JsonContext;
|
|
|
|
/** Get an AccountID from an account ID or public key. */
|
|
std::optional<AccountID>
|
|
accountFromStringStrict(std::string const&);
|
|
|
|
// --> strIdent: public key, account ID, or regular seed.
|
|
// --> bStrict: Only allow account id or public key.
|
|
//
|
|
// Returns a Json::objectValue, containing error information if there was one.
|
|
Json::Value
|
|
accountFromString(
|
|
AccountID& result,
|
|
std::string const& strIdent,
|
|
bool bStrict = false);
|
|
|
|
/** Decode account ID from string
|
|
@param[out] result account ID decoded from string
|
|
@param strIdent public key, account ID, or regular seed.
|
|
@param bStrict Only allow account id or public key.
|
|
@return code representing error, or rpcSUCCES on success
|
|
*/
|
|
error_code_i
|
|
accountFromStringWithCode(
|
|
AccountID& result,
|
|
std::string const& strIdent,
|
|
bool bStrict = false);
|
|
|
|
/** Gets the start hint for traversing account objects
|
|
* @param sle - Ledger entry defined by the marker passed into the RPC.
|
|
* @param accountID - The ID of the account whose objects you are traversing.
|
|
*/
|
|
std::uint64_t
|
|
getStartHint(std::shared_ptr<SLE const> const& sle, AccountID const& accountID);
|
|
|
|
/**
|
|
* Tests if a SLE is owned by accountID.
|
|
* @param ledger - The ledger used to search for the sle.
|
|
* @param sle - The SLE to test for ownership.
|
|
* @param account - The account being tested for SLE ownership.
|
|
*/
|
|
bool
|
|
isRelatedToAccount(
|
|
ReadView const& ledger,
|
|
std::shared_ptr<SLE const> const& sle,
|
|
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 const& 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);
|
|
|
|
bool
|
|
isValidated(
|
|
LedgerMaster& ledgerMaster,
|
|
ReadView const& ledger,
|
|
Application& app);
|
|
|
|
hash_set<AccountID>
|
|
parseAccountIds(Json::Value const& jvArray);
|
|
|
|
bool
|
|
isHexTxID(std::string const& txid);
|
|
|
|
/** Inject JSON describing ledger entry
|
|
|
|
Effects:
|
|
Adds the JSON description of `sle` to `jv`.
|
|
|
|
If `sle` holds an account root, also adds the
|
|
urlgravatar field JSON if sfEmailHash is present.
|
|
*/
|
|
void
|
|
injectSLE(Json::Value& jv, SLE const& sle);
|
|
|
|
/** Retrieve the limit value from a JsonContext, or set a default -
|
|
then restrict the limit by max and min if not an ADMIN request.
|
|
|
|
If there is an error, return it as JSON.
|
|
*/
|
|
std::optional<Json::Value>
|
|
readLimitField(
|
|
unsigned int& limit,
|
|
Tuning::LimitRange const&,
|
|
JsonContext const&);
|
|
|
|
std::optional<Seed>
|
|
getSeedFromRPC(Json::Value const& params, Json::Value& error);
|
|
|
|
std::optional<Seed>
|
|
parseRippleLibSeed(Json::Value const& params);
|
|
|
|
std::pair<PublicKey, SecretKey>
|
|
keypairForSignature(Json::Value const& params, Json::Value& error);
|
|
|
|
/**
|
|
* API version numbers used in API version 1
|
|
*/
|
|
extern beast::SemanticVersion const firstVersion;
|
|
extern beast::SemanticVersion const goodVersion;
|
|
extern beast::SemanticVersion const lastVersion;
|
|
|
|
/**
|
|
* API version numbers used in later API versions
|
|
*
|
|
* Requests with a version number in the range
|
|
* [apiMinimumSupportedVersion, apiMaximumSupportedVersion]
|
|
* are supported.
|
|
*
|
|
* If [beta_rpc_api] is enabled in config, the version numbers
|
|
* in the range [apiMinimumSupportedVersion, apiBetaVersion]
|
|
* are supported.
|
|
*
|
|
* Network Requests without explicit version numbers use
|
|
* apiVersionIfUnspecified. apiVersionIfUnspecified is 1,
|
|
* because all the RPC requests with a version >= 2 must
|
|
* explicitly specify the version in the requests.
|
|
* Note that apiVersionIfUnspecified will be lower than
|
|
* apiMinimumSupportedVersion when we stop supporting API
|
|
* version 1.
|
|
*
|
|
* Command line Requests use apiMaximumSupportedVersion.
|
|
*/
|
|
|
|
constexpr unsigned int apiInvalidVersion = 0;
|
|
constexpr unsigned int apiVersionIfUnspecified = 1;
|
|
constexpr unsigned int apiMinimumSupportedVersion = 1;
|
|
constexpr unsigned int apiMaximumSupportedVersion = 1;
|
|
constexpr unsigned int apiBetaVersion = 2;
|
|
|
|
static_assert(apiMinimumSupportedVersion >= apiVersionIfUnspecified);
|
|
static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion);
|
|
static_assert(apiBetaVersion >= apiMaximumSupportedVersion);
|
|
|
|
template <class Object>
|
|
void
|
|
setVersion(Object& parent, unsigned int apiVersion, bool betaEnabled)
|
|
{
|
|
assert(apiVersion != apiInvalidVersion);
|
|
auto&& object = addObject(parent, jss::version);
|
|
if (apiVersion == apiVersionIfUnspecified)
|
|
{
|
|
object[jss::first] = firstVersion.print();
|
|
object[jss::good] = goodVersion.print();
|
|
object[jss::last] = lastVersion.print();
|
|
}
|
|
else
|
|
{
|
|
object[jss::first] = apiMinimumSupportedVersion;
|
|
object[jss::last] =
|
|
betaEnabled ? apiBetaVersion : apiMaximumSupportedVersion;
|
|
}
|
|
}
|
|
|
|
std::pair<RPC::Status, LedgerEntryType>
|
|
chooseLedgerEntryType(Json::Value const& params);
|
|
|
|
/**
|
|
* Retrieve the api version number from the json value
|
|
*
|
|
* Note that APIInvalidVersion will be returned if
|
|
* 1) the version number field has a wrong format
|
|
* 2) the version number retrieved is out of the supported range
|
|
* 3) the version number is unspecified and
|
|
* APIVersionIfUnspecified is out of the supported range
|
|
*
|
|
* @param value a Json value that may or may not specifies
|
|
* the api version number
|
|
* @param betaEnabled if the beta API version is enabled
|
|
* @return the api version number
|
|
*/
|
|
unsigned int
|
|
getAPIVersionNumber(const Json::Value& value, bool betaEnabled);
|
|
|
|
/** 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
|