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>
31#include <xrpl/protocol/AccountID.h>
32#include <xrpl/protocol/Feature.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>
49 auto const publicKey =
55 result = parseBase58<AccountID>(account);
101 if (sle->getType() == ltRIPPLE_STATE)
103 if (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID)
104 return sle->getFieldU64(sfLowNode);
105 else if (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID)
106 return sle->getFieldU64(sfHighNode);
109 if (!sle->isFieldPresent(sfOwnerNode))
112 return sle->getFieldU64(sfOwnerNode);
121 if (sle->getType() == ltRIPPLE_STATE)
123 return (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID) ||
124 (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID);
126 else if (sle->isFieldPresent(sfAccount))
134 return sle->getAccountID(sfAccount) == accountID ||
135 (sle->isFieldPresent(sfDestination) &&
136 sle->getAccountID(sfDestination) == accountID);
138 else if (sle->getType() == ltSIGNER_LIST)
141 return sle->key() == accountSignerList.
key;
143 else if (sle->getType() == ltNFTOKEN_OFFER)
147 return sle->getAccountID(sfOwner) == accountID;
164 if (!dirIndex.
isZero() && !ledger.
read({ltDIR_NODE, dirIndex}))
170 return it != typeFilter.
end();
175 bool iterateNFTPages =
176 (!typeFilter.has_value() ||
177 typeMatchesFilter(typeFilter.value(), ltNFTOKEN_PAGE)) &&
178 dirIndex == beast::zero;
183 if (iterateNFTPages && entryIndex != beast::zero)
189 iterateNFTPages =
false;
196 uint32_t mlimit = limit;
201 Keylet const first = entryIndex == beast::zero
203 :
Keylet{ltNFTOKEN_PAGE, entryIndex};
211 auto cp = ledger.
read(
Keylet{ltNFTOKEN_PAGE, ck});
216 auto const npm = (*cp)[~sfNextPageMin];
218 cp = ledger.
read(
Keylet(ltNFTOKEN_PAGE, *npm));
226 jvResult[jss::limit] = limit;
242 entryIndex = beast::zero;
254 auto dir = ledger.
read({ltDIR_NODE, dirIndex});
275 auto const& entries = dir->getFieldV256(sfIndexes);
276 auto iter = entries.begin();
280 iter =
std::find(iter, entries.end(), entryIndex);
281 if (iter == entries.end())
289 if (i == mlimit && mlimit < limit)
291 jvResult[jss::limit] = limit;
292 jvResult[jss::marker] =
297 for (; iter != entries.end(); ++iter)
301 if (!typeFilter.has_value() ||
302 typeMatchesFilter(typeFilter.value(), sleNode->getType()))
309 if (++iter != entries.end())
311 jvResult[jss::limit] = limit;
312 jvResult[jss::marker] =
321 auto const nodeIndex = dir->getFieldU64(sfIndexNext);
326 dir = ledger.
read({ltDIR_NODE, dirIndex});
332 auto const& e = dir->getFieldV256(sfIndexes);
335 jvResult[jss::limit] = limit;
336 jvResult[jss::marker] =
362 auto& params = context.params;
364 auto indexValue = params[jss::ledger_index];
365 auto hashValue = params[jss::ledger_hash];
368 auto& legacyLedger = params[jss::ledger];
371 if (legacyLedger.asString().size() > 12)
372 hashValue = legacyLedger;
374 indexValue = legacyLedger;
379 if (!hashValue.isString())
383 if (!ledgerHash.parseHex(hashValue.asString()))
385 return getLedger(ledger, ledgerHash, context);
388 auto const index = indexValue.asString();
390 if (index ==
"current" || index.empty())
391 return getLedger(ledger, LedgerShortcut::CURRENT, context);
393 if (index ==
"validated")
394 return getLedger(ledger, LedgerShortcut::VALIDATED, context);
396 if (index ==
"closed")
397 return getLedger(ledger, LedgerShortcut::CLOSED, context);
407template <
class T,
class R>
411 R& request = context.
params;
437 org::xrpl::rpc::v1::LedgerSpecifier
const& specifier,
442 using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
443 LedgerCase ledgerCase = specifier.ledger_case();
446 case LedgerCase::kHash: {
449 return getLedger(ledger, *hash, context);
453 case LedgerCase::kSequence:
454 return getLedger(ledger, specifier.sequence(), context);
455 case LedgerCase::kShortcut:
457 case LedgerCase::LEDGER_NOT_SET: {
458 auto const shortcut = specifier.shortcut();
460 org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
462 return getLedger(ledger, LedgerShortcut::VALIDATED, context);
467 org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT ||
469 org::xrpl::rpc::v1::LedgerSpecifier::
470 SHORTCUT_UNSPECIFIED)
472 return getLedger(ledger, LedgerShortcut::CURRENT, context);
476 org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
478 return getLedger(ledger, LedgerShortcut::CLOSED, context);
492 if (ledger ==
nullptr)
502 if (ledger ==
nullptr)
505 if (cur->info().seq == ledgerIndex)
511 if (ledger ==
nullptr)
537 if (shortcut == LedgerShortcut::VALIDATED)
540 if (ledger ==
nullptr)
548 !ledger->open(),
"ripple::RPC::getLedger : validated is not open");
552 if (shortcut == LedgerShortcut::CURRENT)
556 ledger->open(),
"ripple::RPC::getLedger : current is open");
558 else if (shortcut == LedgerShortcut::CLOSED)
562 !ledger->open(),
"ripple::RPC::getLedger : closed is not open");
569 if (ledger ==
nullptr)
576 static auto const minSequenceGap = 10;
578 if (ledger->info().seq + minSequenceGap <
631 auto& info = ledger->info();
635 result[jss::ledger_hash] =
to_string(info.hash);
636 result[jss::ledger_index] = info.seq;
640 result[jss::ledger_current_index] = info.seq;
651 if (
auto status =
lookupLedger(ledger, context, result))
652 status.inject(result);
661 for (
auto const& jv : jvArray)
665 auto const id = parseBase58<AccountID>(jv.asString());
677 if (sle.
getType() == ltACCOUNT_ROOT)
682 Blob const b(hash.begin(), hash.end());
684 boost::to_lower(md5);
688 jv[jss::urlgravatar] =
689 str(boost::format(
"http://www.gravatar.com/avatar/%s") % md5);
694 jv[jss::Invalid] =
true;
704 limit =
range.rdefault;
705 if (
auto const& jvLimit = context.
params[jss::limit])
707 if (!(jvLimit.isUInt() || (jvLimit.isInt() && jvLimit.asInt() >= 0)))
710 limit = jvLimit.asUInt();
728 if (result.size() == 18 &&
739 using string_to_seed_t =
743 static seed_match_t
const seedTypes[]{
744 {jss::passphrase.c_str(),
747 [](
std::string const& s) {
return parseBase58<Seed>(s); }},
756 seed_match_t
const* seedType =
nullptr;
758 for (
auto const& t : seedTypes)
770 "Exactly one of the following must be specified: " +
777 auto const& param = params[seedType->first];
778 if (!param.isString())
784 auto const fieldContents = param.asString();
801 bool const has_key_type = params.
isMember(jss::key_type);
804 static char const*
const secretTypes[]{
805 jss::passphrase.c_str(),
808 jss::seed_hex.c_str()};
811 char const* secretType =
nullptr;
813 for (
auto t : secretTypes)
822 if (count == 0 || secretType ==
nullptr)
831 "Exactly one of the following must be specified: " +
843 if (!params[jss::key_type].isString())
862 if (strcmp(secretType, jss::secret.c_str()) == 0)
865 "The secret field is not allowed if " +
876 if (strcmp(secretType, jss::seed_hex.c_str()) != 0)
887 rpcBAD_SEED,
"Specified seed is for an Ed25519 wallet.");
904 if (!params[jss::secret].isString())
926 LogicError(
"keypairForSignature: invalid key type");
937 static constexpr auto types =
938 std::to_array<std::pair<char const*, LedgerEntryType>>({
939#pragma push_macro("LEDGER_ENTRY")
942#define LEDGER_ENTRY(tag, value, name, rpcName, fields) {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");
961 auto const filter = p.asString();
963 types.begin(), types.end(), [&filter](
decltype(types.front())& t) {
964 return t.first == filter;
966 if (iter == types.end())
972 "ripple::RPC::chooseLedgerEntryType : second valid result "
976 result.second = iter->second;
986 case LedgerEntryType::ltAMENDMENTS:
987 case LedgerEntryType::ltDIR_NODE:
988 case LedgerEntryType::ltFEE_SETTINGS:
989 case LedgerEntryType::ltLEDGER_HASHES:
990 case LedgerEntryType::ltNEGATIVE_UNL:
1012 requestedVersion = jv.
get(jss::api_version, requestedVersion);
1014 if (!(requestedVersion.
isInt() || requestedVersion.
isUInt()) ||
1015 requestedVersion < minVersion || requestedVersion > maxVersion)
1017 requestedVersion = invalidVersion;
1019 return requestedVersion.
asUInt();
1026 auto const hasIndex = context.
params.
isMember(jss::ledger_index);
1032 if ((hasHash && hasIndex) || !(hasHash || hasIndex))
1035 "Exactly one of ledger_hash and ledger_index can be set.");
1042 auto const& jsonHash = context.
params[jss::ledger_hash];
1043 if (!jsonHash.isString() || !ledgerHash.
parseHex(jsonHash.asString()))
1048 auto const& jsonIndex = context.
params[jss::ledger_index];
1049 if (!jsonIndex.isInt())
1061 ledgerIndex = jsonIndex.asInt();
1064 if (ledgerIndex >= ledger->info().seq)
1066 if (ledgerIndex <= 0)
1069 auto const j = context.
app.
journal(
"RPCHandler");
1072 auto neededHash =
hashOfSeq(*ledger, ledgerIndex, j);
1078 auto refHash =
hashOfSeq(*ledger, refIndex, j);
1081 "ripple::RPC::getLedgerByContext : nonzero ledger hash");
1094 "acquiring ledger containing requested index");
1095 jvResult[jss::acquiring] =
1104 "acquiring ledger containing requested index");
1105 jvResult[jss::acquiring] = il->getJson(0);
1113 neededHash =
hashOfSeq(*ledger, ledgerIndex, j);
1117 "ripple::RPC::getLedgerByContext : nonzero needed hash");
1118 ledgerHash = neededHash ? *neededHash : beast::zero;
1134 return il->getJson(0);
1137 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.