20#include <xrpld/app/ledger/LedgerMaster.h>
21#include <xrpld/app/ledger/LedgerToJson.h>
22#include <xrpld/app/ledger/OpenLedger.h>
23#include <xrpld/app/misc/Transaction.h>
24#include <xrpld/app/paths/TrustLine.h>
25#include <xrpld/app/rdb/RelationalDatabase.h>
26#include <xrpld/app/tx/detail/NFTokenUtils.h>
27#include <xrpld/ledger/View.h>
28#include <xrpld/rpc/Context.h>
29#include <xrpld/rpc/DeliveredAmount.h>
30#include <xrpld/rpc/detail/RPCHelpers.h>
32#include <xrpl/protocol/AccountID.h>
33#include <xrpl/protocol/RPCErr.h>
34#include <xrpl/protocol/nftPageMask.h>
35#include <xrpl/resource/Fees.h>
37#include <boost/algorithm/string/case_conv.hpp>
38#include <boost/algorithm/string/predicate.hpp>
48 auto const publicKey =
54 result = parseBase58<AccountID>(account);
100 if (sle->getType() == ltRIPPLE_STATE)
102 if (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID)
103 return sle->getFieldU64(sfLowNode);
104 else if (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID)
105 return sle->getFieldU64(sfHighNode);
108 if (!sle->isFieldPresent(sfOwnerNode))
111 return sle->getFieldU64(sfOwnerNode);
120 if (sle->getType() == ltRIPPLE_STATE)
122 return (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID) ||
123 (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID);
125 else if (sle->isFieldPresent(sfAccount))
133 return sle->getAccountID(sfAccount) == accountID ||
134 (sle->isFieldPresent(sfDestination) &&
135 sle->getAccountID(sfDestination) == accountID);
137 else if (sle->getType() == ltSIGNER_LIST)
140 return sle->key() == accountSignerList.
key;
142 else if (sle->getType() == ltNFTOKEN_OFFER)
146 return sle->getAccountID(sfOwner) == accountID;
163 if (!dirIndex.
isZero() && !ledger.
read({ltDIR_NODE, dirIndex}))
169 return it != typeFilter.
end();
174 bool iterateNFTPages =
175 (!typeFilter.has_value() ||
176 typeMatchesFilter(typeFilter.value(), ltNFTOKEN_PAGE)) &&
177 dirIndex == beast::zero;
182 if (iterateNFTPages && entryIndex != beast::zero)
188 iterateNFTPages =
false;
195 uint32_t mlimit = limit;
200 Keylet const first = entryIndex == beast::zero
202 :
Keylet{ltNFTOKEN_PAGE, entryIndex};
210 auto cp = ledger.
read(
Keylet{ltNFTOKEN_PAGE, ck});
215 auto const npm = (*cp)[~sfNextPageMin];
217 cp = ledger.
read(
Keylet(ltNFTOKEN_PAGE, *npm));
225 jvResult[jss::limit] = limit;
241 entryIndex = beast::zero;
253 auto dir = ledger.
read({ltDIR_NODE, dirIndex});
274 auto const& entries = dir->getFieldV256(sfIndexes);
275 auto iter = entries.begin();
279 iter =
std::find(iter, entries.end(), entryIndex);
280 if (iter == entries.end())
288 if (i == mlimit && mlimit < limit)
290 jvResult[jss::limit] = limit;
291 jvResult[jss::marker] =
296 for (; iter != entries.end(); ++iter)
300 if (!typeFilter.has_value() ||
301 typeMatchesFilter(typeFilter.value(), sleNode->getType()))
308 if (++iter != entries.end())
310 jvResult[jss::limit] = limit;
311 jvResult[jss::marker] =
320 auto const nodeIndex = dir->getFieldU64(sfIndexNext);
325 dir = ledger.
read({ltDIR_NODE, dirIndex});
331 auto const& e = dir->getFieldV256(sfIndexes);
334 jvResult[jss::limit] = limit;
335 jvResult[jss::marker] =
361 auto& params = context.params;
363 auto indexValue = params[jss::ledger_index];
364 auto hashValue = params[jss::ledger_hash];
367 auto& legacyLedger = params[jss::ledger];
370 if (legacyLedger.asString().size() > 12)
371 hashValue = legacyLedger;
373 indexValue = legacyLedger;
378 if (!hashValue.isString())
382 if (!ledgerHash.parseHex(hashValue.asString()))
384 return getLedger(ledger, ledgerHash, context);
387 auto const index = indexValue.asString();
389 if (index ==
"current" || index.empty())
390 return getLedger(ledger, LedgerShortcut::CURRENT, context);
392 if (index ==
"validated")
393 return getLedger(ledger, LedgerShortcut::VALIDATED, context);
395 if (index ==
"closed")
396 return getLedger(ledger, LedgerShortcut::CLOSED, context);
406template <
class T,
class R>
410 R& request = context.
params;
436 org::xrpl::rpc::v1::LedgerSpecifier
const& specifier,
441 using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
442 LedgerCase ledgerCase = specifier.ledger_case();
445 case LedgerCase::kHash: {
448 return getLedger(ledger, *hash, context);
452 case LedgerCase::kSequence:
453 return getLedger(ledger, specifier.sequence(), context);
454 case LedgerCase::kShortcut:
456 case LedgerCase::LEDGER_NOT_SET: {
457 auto const shortcut = specifier.shortcut();
459 org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
461 return getLedger(ledger, LedgerShortcut::VALIDATED, context);
466 org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT ||
468 org::xrpl::rpc::v1::LedgerSpecifier::
469 SHORTCUT_UNSPECIFIED)
471 return getLedger(ledger, LedgerShortcut::CURRENT, context);
475 org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
477 return getLedger(ledger, LedgerShortcut::CLOSED, context);
491 if (ledger ==
nullptr)
501 if (ledger ==
nullptr)
504 if (cur->info().seq == ledgerIndex)
510 if (ledger ==
nullptr)
536 if (shortcut == LedgerShortcut::VALIDATED)
539 if (ledger ==
nullptr)
547 !ledger->open(),
"ripple::RPC::getLedger : validated is not open");
551 if (shortcut == LedgerShortcut::CURRENT)
555 ledger->open(),
"ripple::RPC::getLedger : current is open");
557 else if (shortcut == LedgerShortcut::CLOSED)
561 !ledger->open(),
"ripple::RPC::getLedger : closed is not open");
568 if (ledger ==
nullptr)
575 static auto const minSequenceGap = 10;
577 if (ledger->info().seq + minSequenceGap <
630 auto& info = ledger->info();
634 result[jss::ledger_hash] =
to_string(info.hash);
635 result[jss::ledger_index] = info.seq;
639 result[jss::ledger_current_index] = info.seq;
650 if (
auto status =
lookupLedger(ledger, context, result))
651 status.inject(result);
660 for (
auto const& jv : jvArray)
664 auto const id = parseBase58<AccountID>(jv.asString());
676 if (sle.
getType() == ltACCOUNT_ROOT)
681 Blob const b(hash.begin(), hash.end());
683 boost::to_lower(md5);
687 jv[jss::urlgravatar] =
688 str(boost::format(
"http://www.gravatar.com/avatar/%s") % md5);
693 jv[jss::Invalid] =
true;
703 limit =
range.rdefault;
704 if (
auto const& jvLimit = context.
params[jss::limit])
706 if (!(jvLimit.isUInt() || (jvLimit.isInt() && jvLimit.asInt() >= 0)))
709 limit = jvLimit.asUInt();
727 if (result.size() == 18 &&
738 using string_to_seed_t =
742 static seed_match_t
const seedTypes[]{
743 {jss::passphrase.c_str(),
746 [](
std::string const& s) {
return parseBase58<Seed>(s); }},
755 seed_match_t
const* seedType =
nullptr;
757 for (
auto const& t : seedTypes)
769 "Exactly one of the following must be specified: " +
776 auto const& param = params[seedType->first];
777 if (!param.isString())
783 auto const fieldContents = param.asString();
800 bool const has_key_type = params.
isMember(jss::key_type);
803 static char const*
const secretTypes[]{
804 jss::passphrase.c_str(),
807 jss::seed_hex.c_str()};
810 char const* secretType =
nullptr;
812 for (
auto t : secretTypes)
821 if (count == 0 || secretType ==
nullptr)
830 "Exactly one of the following must be specified: " +
842 if (!params[jss::key_type].isString())
861 if (strcmp(secretType, jss::secret.c_str()) == 0)
864 "The secret field is not allowed if " +
875 if (strcmp(secretType, jss::seed_hex.c_str()) != 0)
886 rpcBAD_SEED,
"Specified seed is for an Ed25519 wallet.");
903 if (!params[jss::secret].isString())
925 LogicError(
"keypairForSignature: invalid key type");
936 static constexpr auto types = std::to_array<
938#pragma push_macro("LEDGER_ENTRY")
941#define LEDGER_ENTRY(tag, value, name, rpcName, fields) \
942 {jss::name, jss::rpcName, tag},
944#include <xrpl/protocol/detail/ledger_entries.macro>
947#pragma pop_macro("LEDGER_ENTRY")
950 auto const& p = params[jss::type];
957 "ripple::RPC::chooseLedgerEntryType : first valid result type");
964 auto const filter = p.asString();
966 std::ranges::find_if(types, [&filter](
decltype(types.front())& t) {
967 return boost::iequals(std::get<0>(t), filter) ||
968 std::get<1>(t) == filter;
970 if (iter == types.end())
976 "ripple::RPC::chooseLedgerEntryType : second valid result "
980 result.second = std::get<2>(*iter);
990 case LedgerEntryType::ltAMENDMENTS:
991 case LedgerEntryType::ltDIR_NODE:
992 case LedgerEntryType::ltFEE_SETTINGS:
993 case LedgerEntryType::ltLEDGER_HASHES:
994 case LedgerEntryType::ltNEGATIVE_UNL:
1016 requestedVersion = jv.
get(jss::api_version, requestedVersion);
1018 if (!(requestedVersion.
isInt() || requestedVersion.
isUInt()) ||
1019 requestedVersion < minVersion || requestedVersion > maxVersion)
1021 requestedVersion = invalidVersion;
1023 return requestedVersion.
asUInt();
1030 auto const hasIndex = context.
params.
isMember(jss::ledger_index);
1036 if ((hasHash && hasIndex) || !(hasHash || hasIndex))
1039 "Exactly one of ledger_hash and ledger_index can be set.");
1046 auto const& jsonHash = context.
params[jss::ledger_hash];
1047 if (!jsonHash.isString() || !ledgerHash.
parseHex(jsonHash.asString()))
1052 auto const& jsonIndex = context.
params[jss::ledger_index];
1053 if (!jsonIndex.isInt())
1065 ledgerIndex = jsonIndex.asInt();
1068 if (ledgerIndex >= ledger->info().seq)
1070 if (ledgerIndex <= 0)
1073 auto const j = context.
app.
journal(
"RPCHandler");
1076 auto neededHash =
hashOfSeq(*ledger, ledgerIndex, j);
1082 auto refHash =
hashOfSeq(*ledger, refIndex, j);
1085 "ripple::RPC::getLedgerByContext : nonzero ledger hash");
1098 "acquiring ledger containing requested index");
1099 jvResult[jss::acquiring] =
1108 "acquiring ledger containing requested index");
1109 jvResult[jss::acquiring] = il->getJson(0);
1117 neededHash =
hashOfSeq(*ledger, ledgerIndex, j);
1121 "ripple::RPC::getLedgerByContext : nonzero needed hash");
1122 ledgerHash = neededHash ? *neededHash : beast::zero;
1138 return il->getJson(0);
1141 rpcNOT_READY,
"findCreate failed to return an inbound ledger");
Value get(UInt index, const Value &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
std::string asString() const
Returns the unquoted string value.
bool isMember(const char *key) const
Return true if the object has a member named key.
A Semantic Version number.
virtual Config & config()=0
virtual beast::Journal journal(std::string const &name)=0
virtual InboundLedgers & getInboundLedgers()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason)=0
virtual std::shared_ptr< InboundLedger > find(LedgerHash const &hash)=0
std::shared_ptr< Ledger const > getValidatedLedger()
std::shared_ptr< ReadView const > getCurrentLedger()
bool isValidated(ReadView const &ledger)
std::shared_ptr< Ledger const > getClosedLedger()
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
std::shared_ptr< Ledger const > getLedgerByHash(uint256 const &hash)
LedgerIndex getValidLedgerIndex()
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual std::optional< key_type > succ(key_type const &key, std::optional< key_type > const &last=std::nullopt) const =0
Return the key of the next state item.
LedgerEntryType getType() const
Json::Value getJson(JsonOptions options) const override
bool isFieldPresent(SField const &field) const
uint128 getFieldH128(SField const &field) const
Seeds are used to generate deterministic secret keys.
An immutable linear range of bytes.
static std::optional< base_uint > fromVoidChecked(T const &from)
static constexpr std::size_t size()
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Status
Return codes from Backend operations.
auto constexpr maxValidatedLedgerAge
beast::SemanticVersion const firstVersion("1.0.0")
API version numbers used in API version 1.
Status ledgerFromRequest(T &ledger, GRPCContext< R > &context)
unsigned int getAPIVersionNumber(Json::Value const &jv, bool betaEnabled)
Retrieve the api version number from the json value.
error_code_i accountFromStringWithCode(AccountID &result, std::string const &strIdent, bool bStrict)
Decode account ID from string.
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
std::optional< Seed > getSeedFromRPC(Json::Value const ¶ms, Json::Value &error)
beast::SemanticVersion const lastVersion("1.0.0")
static constexpr auto apiMaximumSupportedVersion
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
std::variant< std::shared_ptr< Ledger const >, Json::Value > getLedgerByContext(RPC::JsonContext &context)
Return a ledger based on ledger_hash or ledger_index, or an RPC error.
Json::Value invalid_field_error(std::string const &name)
bool isAccountObjectsValidType(LedgerEntryType const &type)
Check if the type is a valid filtering type for account_objects method.
static constexpr std::integral_constant< unsigned, Version > apiVersion
bool isRelatedToAccount(ReadView const &ledger, std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Tests if a SLE is owned by accountID.
void injectSLE(Json::Value &jv, SLE const &sle)
Inject JSON describing ledger entry.
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Status getLedger(T &ledger, uint256 const &ledgerHash, Context &context)
Get ledger by hash If there is no error in the return value, the ledger pointer will have been filled...
static constexpr auto apiBetaVersion
std::pair< RPC::Status, LedgerEntryType > chooseLedgerEntryType(Json::Value const ¶ms)
std::string invalid_field_message(std::string const &name)
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)
Gathers all objects for an account in a ledger.
std::optional< Json::Value > readLimitField(unsigned int &limit, Tuning::LimitRange const &range, JsonContext const &context)
Retrieve the limit value from a JsonContext, or set a default - then restrict the limit by max and mi...
beast::SemanticVersion const goodVersion("1.0.0")
Json::Value accountFromString(AccountID &result, std::string const &strIdent, bool bStrict)
static constexpr auto apiVersionIfUnspecified
Json::Value expected_field_error(std::string const &name, std::string const &type)
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext &context, Json::Value &result)
Look up a ledger from a request and fill a Json::Result with the data representing a ledger.
Status ledgerFromSpecifier(T &ledger, org::xrpl::rpc::v1::LedgerSpecifier const &specifier, Context &context)
std::optional< AccountID > accountFromStringStrict(std::string const &account)
Get an AccountID from an account ID or public key.
hash_set< AccountID > parseAccountIds(Json::Value const &jvArray)
static constexpr auto apiInvalidVersion
std::uint64_t getStartHint(std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Gets the start hint for traversing account objects.
static constexpr auto apiMinimumSupportedVersion
Json::Value missing_field_error(std::string const &name)
std::optional< Seed > parseRippleLibSeed(Json::Value const &value)
std::optional< std::pair< PublicKey, SecretKey > > keypairForSignature(Json::Value const ¶ms, Json::Value &error, unsigned int apiVersion)
Charge const feeHeavyBurdenRPC
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet signers(AccountID const &account) noexcept
A SignerList.
uint256 constexpr pageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::optional< KeyType > keyTypeFromString(std::string const &s)
LedgerIndex getCandidateLedger(LedgerIndex requested)
Find a ledger index from which we could easily get the requested ledger.
std::pair< PublicKey, SecretKey > generateKeyPair(KeyType type, Seed const &seed)
Generate a key pair deterministically.
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
AccountID calcAccountID(PublicKey const &pk)
Json::Value rpcError(int iError)
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
std::string decodeBase58Token(std::string const &s, TokenType type)
std::string strHex(FwdIt begin, FwdIt end)
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
std::string to_string(base_uint< Bits, Tag > const &a)
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
LedgerEntryType
Identifiers for on-ledger objects.
@ ltANY
A special type, matching any ledger entry type.
Number root(Number f, unsigned d)
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
@ ledgerMaster
ledger master data for signing
std::optional< Seed > parseGenericSeed(std::string const &str, bool rfc1751=true)
Attempt to parse a string as a seed.
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
A pair of SHAMap key and LedgerEntryType.
The context of information needed to call an RPC.
Resource::Charge & loadType
LedgerMaster & ledgerMaster
Status represents the results of an operation that might fail.
Represents RPC limit parameter values that have a min, default and max.