rippled
Loading...
Searching...
No Matches
RPCHelpers.cpp
1#include <xrpld/app/misc/Transaction.h>
2#include <xrpld/app/paths/TrustLine.h>
3#include <xrpld/app/rdb/RelationalDatabase.h>
4#include <xrpld/app/tx/detail/NFTokenUtils.h>
5#include <xrpld/rpc/Context.h>
6#include <xrpld/rpc/DeliveredAmount.h>
7#include <xrpld/rpc/detail/RPCHelpers.h>
8
9#include <xrpl/ledger/View.h>
10#include <xrpl/protocol/AccountID.h>
11#include <xrpl/protocol/RPCErr.h>
12#include <xrpl/protocol/nftPageMask.h>
13#include <xrpl/resource/Fees.h>
14
15#include <boost/algorithm/string/case_conv.hpp>
16#include <boost/algorithm/string/predicate.hpp>
17
18namespace xrpl {
19namespace RPC {
20
23{
24 if (sle->getType() == ltRIPPLE_STATE)
25 {
26 if (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID)
27 return sle->getFieldU64(sfLowNode);
28 else if (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID)
29 return sle->getFieldU64(sfHighNode);
30 }
31
32 if (!sle->isFieldPresent(sfOwnerNode))
33 return 0;
34
35 return sle->getFieldU64(sfOwnerNode);
36}
37
38bool
40 ReadView const& ledger,
42 AccountID const& accountID)
43{
44 if (sle->getType() == ltRIPPLE_STATE)
45 {
46 return (sle->getFieldAmount(sfLowLimit).getIssuer() == accountID) ||
47 (sle->getFieldAmount(sfHighLimit).getIssuer() == accountID);
48 }
49 else if (sle->isFieldPresent(sfAccount))
50 {
51 // If there's an sfAccount present, also test the sfDestination, if
52 // present. This will match objects such as Escrows (ltESCROW), Payment
53 // Channels (ltPAYCHAN), and Checks (ltCHECK) because those are added to
54 // the Destination account's directory. It intentionally EXCLUDES
55 // NFToken Offers (ltNFTOKEN_OFFER). NFToken Offers are NOT added to the
56 // Destination account's directory.
57 return sle->getAccountID(sfAccount) == accountID ||
58 (sle->isFieldPresent(sfDestination) &&
59 sle->getAccountID(sfDestination) == accountID);
60 }
61 else if (sle->getType() == ltSIGNER_LIST)
62 {
63 Keylet const accountSignerList = keylet::signers(accountID);
64 return sle->key() == accountSignerList.key;
65 }
66 else if (sle->getType() == ltNFTOKEN_OFFER)
67 {
68 // Do not check the sfDestination field. NFToken Offers are NOT added to
69 // the Destination account's directory.
70 return sle->getAccountID(sfOwner) == accountID;
71 }
72
73 return false;
74}
75
78{
80 for (auto const& jv : jvArray)
81 {
82 if (!jv.isString())
83 return hash_set<AccountID>();
84 auto const id = parseBase58<AccountID>(jv.asString());
85 if (!id)
86 return hash_set<AccountID>();
87 result.insert(*id);
88 }
89 return result;
90}
91
94 unsigned int& limit,
96 JsonContext const& context)
97{
98 limit = range.rDefault;
99 if (!context.params.isMember(jss::limit) ||
100 context.params[jss::limit].isNull())
101 return std::nullopt;
102
103 auto const& jvLimit = context.params[jss::limit];
104 if (!(jvLimit.isUInt() || (jvLimit.isInt() && jvLimit.asInt() >= 0)))
105 return RPC::expected_field_error(jss::limit, "unsigned integer");
106
107 limit = jvLimit.asUInt();
108 if (limit == 0)
109 return RPC::invalid_field_error(jss::limit);
110
111 if (!isUnlimited(context.role))
112 limit = std::max(range.rmin, std::min(range.rmax, limit));
113
114 return std::nullopt;
115}
116
119{
120 // ripple-lib encodes seed used to generate an Ed25519 wallet in a
121 // non-standard way. While rippled never encode seeds that way, we
122 // try to detect such keys to avoid user confusion.
123 if (!value.isString())
124 return std::nullopt;
125
126 auto const result = decodeBase58Token(value.asString(), TokenType::None);
127
128 if (result.size() == 18 &&
129 static_cast<std::uint8_t>(result[0]) == std::uint8_t(0xE1) &&
130 static_cast<std::uint8_t>(result[1]) == std::uint8_t(0x4B))
131 return Seed(makeSlice(result.substr(2)));
132
133 return std::nullopt;
134}
135
138{
139 using string_to_seed_t =
141 using seed_match_t = std::pair<char const*, string_to_seed_t>;
142
143 static seed_match_t const seedTypes[]{
144 {jss::passphrase.c_str(),
145 [](std::string const& s) { return parseGenericSeed(s); }},
146 {jss::seed.c_str(),
147 [](std::string const& s) { return parseBase58<Seed>(s); }},
148 {jss::seed_hex.c_str(), [](std::string const& s) {
149 uint128 i;
150 if (i.parseHex(s))
151 return std::optional<Seed>(Slice(i.data(), i.size()));
152 return std::optional<Seed>{};
153 }}};
154
155 // Identify which seed type is in use.
156 seed_match_t const* seedType = nullptr;
157 int count = 0;
158 for (auto const& t : seedTypes)
159 {
160 if (params.isMember(t.first))
161 {
162 ++count;
163 seedType = &t;
164 }
165 }
166
167 if (count != 1)
168 {
169 error = RPC::make_param_error(
170 "Exactly one of the following must be specified: " +
171 std::string(jss::passphrase) + ", " + std::string(jss::seed) +
172 " or " + std::string(jss::seed_hex));
173 return std::nullopt;
174 }
175
176 // Make sure a string is present
177 auto const& param = params[seedType->first];
178 if (!param.isString())
179 {
180 error = RPC::expected_field_error(seedType->first, "string");
181 return std::nullopt;
182 }
183
184 auto const fieldContents = param.asString();
185
186 // Convert string to seed.
187 std::optional<Seed> seed = seedType->second(fieldContents);
188
189 if (!seed)
190 error = rpcError(rpcBAD_SEED);
191
192 return seed;
193}
194
197 Json::Value const& params,
198 Json::Value& error,
199 unsigned int apiVersion)
200{
201 bool const has_key_type = params.isMember(jss::key_type);
202
203 // All of the secret types we allow, but only one at a time.
204 static char const* const secretTypes[]{
205 jss::passphrase.c_str(),
206 jss::secret.c_str(),
207 jss::seed.c_str(),
208 jss::seed_hex.c_str()};
209
210 // Identify which secret type is in use.
211 char const* secretType = nullptr;
212 int count = 0;
213 for (auto t : secretTypes)
214 {
215 if (params.isMember(t))
216 {
217 ++count;
218 secretType = t;
219 }
220 }
221
222 if (count == 0 || secretType == nullptr)
223 {
224 error = RPC::missing_field_error(jss::secret);
225 return {};
226 }
227
228 if (count > 1)
229 {
230 error = RPC::make_param_error(
231 "Exactly one of the following must be specified: " +
232 std::string(jss::passphrase) + ", " + std::string(jss::secret) +
233 ", " + std::string(jss::seed) + " or " +
234 std::string(jss::seed_hex));
235 return {};
236 }
237
240
241 if (has_key_type)
242 {
243 if (!params[jss::key_type].isString())
244 {
245 error = RPC::expected_field_error(jss::key_type, "string");
246 return {};
247 }
248
249 keyType = keyTypeFromString(params[jss::key_type].asString());
250
251 if (!keyType)
252 {
253 if (apiVersion > 1u)
255 else
256 error = RPC::invalid_field_error(jss::key_type);
257 return {};
258 }
259
260 // using strcmp as pointers may not match (see
261 // https://developercommunity.visualstudio.com/t/assigning-constexpr-char--to-static-cha/10021357?entry=problem)
262 if (strcmp(secretType, jss::secret.c_str()) == 0)
263 {
264 error = RPC::make_param_error(
265 "The secret field is not allowed if " +
266 std::string(jss::key_type) + " is used.");
267 return {};
268 }
269 }
270
271 // ripple-lib encodes seed used to generate an Ed25519 wallet in a
272 // non-standard way. While we never encode seeds that way, we try
273 // to detect such keys to avoid user confusion.
274 // using strcmp as pointers may not match (see
275 // https://developercommunity.visualstudio.com/t/assigning-constexpr-char--to-static-cha/10021357?entry=problem)
276 if (strcmp(secretType, jss::seed_hex.c_str()) != 0)
277 {
278 seed = RPC::parseRippleLibSeed(params[secretType]);
279
280 if (seed)
281 {
282 // If the user passed in an Ed25519 seed but *explicitly*
283 // requested another key type, return an error.
285 {
286 error = RPC::make_error(
287 rpcBAD_SEED, "Specified seed is for an Ed25519 wallet.");
288 return {};
289 }
290
291 keyType = KeyType::ed25519;
292 }
293 }
294
295 if (!keyType)
296 keyType = KeyType::secp256k1;
297
298 if (!seed)
299 {
300 if (has_key_type)
301 seed = getSeedFromRPC(params, error);
302 else
303 {
304 if (!params[jss::secret].isString())
305 {
306 error = RPC::expected_field_error(jss::secret, "string");
307 return {};
308 }
309
310 seed = parseGenericSeed(params[jss::secret].asString());
311 }
312 }
313
314 if (!seed)
315 {
316 if (!contains_error(error))
317 {
318 error = RPC::make_error(
320 }
321
322 return {};
323 }
324
325 if (keyType != KeyType::secp256k1 && keyType != KeyType::ed25519)
326 LogicError("keypairForSignature: invalid key type");
327
328 return generateKeyPair(*keyType, *seed);
329}
330
333{
335 if (params.isMember(jss::type))
336 {
337 static constexpr auto types = std::to_array<
339#pragma push_macro("LEDGER_ENTRY")
340#undef LEDGER_ENTRY
341
342#define LEDGER_ENTRY(tag, value, name, rpcName, ...) \
343 {jss::name, jss::rpcName, tag},
344
345#include <xrpl/protocol/detail/ledger_entries.macro>
346
347#undef LEDGER_ENTRY
348#pragma pop_macro("LEDGER_ENTRY")
349 });
350
351 auto const& p = params[jss::type];
352 if (!p.isString())
353 {
354 result.first = RPC::Status{
355 rpcINVALID_PARAMS, "Invalid field 'type', not string."};
356 XRPL_ASSERT(
357 result.first.type() == RPC::Status::Type::error_code_i,
358 "xrpl::RPC::chooseLedgerEntryType : first valid result type");
359 return result;
360 }
361
362 // Use the passed in parameter to find a ledger type based on matching
363 // against the canonical name (case-insensitive) or the RPC name
364 // (case-sensitive).
365 auto const filter = p.asString();
366 auto const iter =
367 std::ranges::find_if(types, [&filter](decltype(types.front())& t) {
368 return boost::iequals(std::get<0>(t), filter) ||
369 std::get<1>(t) == filter;
370 });
371 if (iter == types.end())
372 {
373 result.first =
374 RPC::Status{rpcINVALID_PARAMS, "Invalid field 'type'."};
375 XRPL_ASSERT(
376 result.first.type() == RPC::Status::Type::error_code_i,
377 "xrpl::RPC::chooseLedgerEntryType : second valid result "
378 "type");
379 return result;
380 }
381 result.second = std::get<2>(*iter);
382 }
383 return result;
384}
385
386bool
388{
389 switch (type)
390 {
391 case LedgerEntryType::ltAMENDMENTS:
392 case LedgerEntryType::ltDIR_NODE:
393 case LedgerEntryType::ltFEE_SETTINGS:
394 case LedgerEntryType::ltLEDGER_HASHES:
395 case LedgerEntryType::ltNEGATIVE_UNL:
396 return false;
397 default:
398 return true;
399 }
400}
401
402} // namespace RPC
403} // namespace xrpl
Represents a JSON value.
Definition json_value.h:131
bool isString() const
std::string asString() const
Returns the unquoted string value.
bool isNull() const
isNull() tests to see if this field is null.
bool isMember(char const *key) const
Return true if the object has a member named key.
A view into a ledger.
Definition ReadView.h:32
Seeds are used to generate deterministic secret keys.
Definition Seed.h:15
An immutable linear range of bytes.
Definition Slice.h:27
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:484
pointer data()
Definition base_uint.h:106
static constexpr std::size_t size()
Definition base_uint.h:507
T find_if(T... args)
T insert(T... args)
T is_same_v
T max(T... args)
T min(T... args)
Json::Value invalid_field_error(std::string const &name)
Definition ErrorCodes.h:284
bool isRelatedToAccount(ReadView const &ledger, std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Tests if a ledger entry (SLE) is owned by the specified account.
std::pair< RPC::Status, LedgerEntryType > chooseLedgerEntryType(Json::Value const &params)
Chooses the ledger entry type based on RPC parameters.
bool isAccountObjectsValidType(LedgerEntryType const &type)
Checks if the type is a valid filtering type for the account_objects method.
std::uint64_t getStartHint(std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Gets the start hint for traversing account objects.
Json::Value expected_field_error(std::string const &name, std::string const &type)
Definition ErrorCodes.h:308
Json::Value missing_field_error(std::string const &name)
Definition ErrorCodes.h:242
std::optional< Seed > parseRippleLibSeed(Json::Value const &value)
Parses a RippleLib seed from RPC parameters.
hash_set< AccountID > parseAccountIds(Json::Value const &jvArray)
Parses an array of account IDs from a JSON value.
static constexpr std::integral_constant< unsigned, Version > apiVersion
Definition ApiVersion.h:39
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Definition ErrorCodes.h:230
std::string invalid_field_message(std::string const &name)
Definition ErrorCodes.h:272
std::optional< std::pair< PublicKey, SecretKey > > keypairForSignature(Json::Value const &params, Json::Value &error, unsigned int apiVersion)
Generates a keypair for signature from RPC parameters.
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
std::optional< Seed > getSeedFromRPC(Json::Value const &params, Json::Value &error)
Extracts a Seed from RPC parameters.
std::optional< Json::Value > readLimitField(unsigned int &limit, Tuning::LimitRange const &range, JsonContext const &context)
Retrieves the limit value from a JsonContext or sets a default.
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Keylet signers(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:312
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
std::optional< KeyType > keyTypeFromString(std::string const &s)
Definition KeyType.h:15
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:35
std::pair< PublicKey, SecretKey > generateKeyPair(KeyType type, Seed const &seed)
Generate a key pair deterministically.
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
LedgerEntryType
Identifiers for on-ledger objects.
@ ltANY
A special type, matching any ledger entry type.
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)
Definition Slice.h:225
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:106
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:191
@ rpcBAD_KEY_TYPE
Definition ErrorCodes.h:114
@ rpcBAD_SEED
Definition ErrorCodes.h:80
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:65
std::optional< Seed > parseGenericSeed(std::string const &str, bool rfc1751=true)
Attempt to parse a string as a seed.
Definition Seed.cpp:78
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
Json::Value params
Definition Context.h:44
Status represents the results of an operation that might fail.
Definition Status.h:21
static constexpr Code OK
Definition Status.h:27
Represents RPC limit parameter values that have a min, default and max.
T value_or(T... args)