rippled
Loading...
Searching...
No Matches
AccountObjects.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2014 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/main/Application.h>
21#include <xrpld/app/tx/detail/NFTokenUtils.h>
22#include <xrpld/ledger/ReadView.h>
23#include <xrpld/rpc/Context.h>
24#include <xrpld/rpc/detail/RPCHelpers.h>
25#include <xrpld/rpc/detail/Tuning.h>
26#include <xrpl/protocol/ErrorCodes.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/LedgerFormats.h>
29#include <xrpl/protocol/RPCErr.h>
30#include <xrpl/protocol/jss.h>
31#include <xrpl/protocol/nftPageMask.h>
32#include <xrpl/resource/Fees.h>
33
34#include <sstream>
35#include <string>
36
37namespace ripple {
38
52{
53 auto const& params = context.params;
54 if (!params.isMember(jss::account))
55 return RPC::missing_field_error(jss::account);
56
57 if (!params[jss::account].isString())
58 return RPC::invalid_field_error(jss::account);
59
60 auto id = parseBase58<AccountID>(params[jss::account].asString());
61 if (!id)
62 {
64 }
65
67 auto result = RPC::lookupLedger(ledger, context);
68 if (ledger == nullptr)
69 return result;
70 auto const accountID{id.value()};
71
72 if (!ledger->exists(keylet::account(accountID)))
74
75 unsigned int limit;
76 if (auto err = readLimitField(limit, RPC::Tuning::accountNFTokens, context))
77 return *err;
78
79 uint256 marker;
80 bool const markerSet = params.isMember(jss::marker);
81
82 if (markerSet)
83 {
84 auto const& m = params[jss::marker];
85 if (!m.isString())
86 return RPC::expected_field_error(jss::marker, "string");
87
88 if (!marker.parseHex(m.asString()))
89 return RPC::invalid_field_error(jss::marker);
90 }
91
92 auto const first = keylet::nftpage(keylet::nftpage_min(accountID), marker);
93 auto const last = keylet::nftpage_max(accountID);
94
95 auto cp = ledger->read(Keylet(
96 ltNFTOKEN_PAGE,
97 ledger->succ(first.key, last.key.next()).value_or(last.key)));
98
99 std::uint32_t cnt = 0;
100 auto& nfts = (result[jss::account_nfts] = Json::arrayValue);
101
102 // Continue iteration from the current page:
103 bool pastMarker = marker.isZero();
104 bool markerFound = false;
105 uint256 const maskedMarker = marker & nft::pageMask;
106 while (cp)
107 {
108 auto arr = cp->getFieldArray(sfNFTokens);
109
110 for (auto const& o : arr)
111 {
112 // Scrolling past the marker gets weird. We need to look at
113 // a couple of conditions.
114 //
115 // 1. If the low 96-bits don't match, then we compare only
116 // against the low 96-bits, since that's what determines
117 // the sort order of the pages.
118 //
119 // 2. However, within one page there can be a number of
120 // NFTokenIDs that all have the same low 96 bits. If we're
121 // in that case then we need to compare against the full
122 // 256 bits.
123 uint256 const nftokenID = o[sfNFTokenID];
124 uint256 const maskedNftokenID = nftokenID & nft::pageMask;
125
126 if (!pastMarker)
127 {
128 if (maskedNftokenID < maskedMarker)
129 continue;
130
131 if (maskedNftokenID == maskedMarker && nftokenID < marker)
132 continue;
133
134 if (nftokenID == marker)
135 {
136 markerFound = true;
137 continue;
138 }
139 }
140
141 if (markerSet && !markerFound)
142 return RPC::invalid_field_error(jss::marker);
143
144 pastMarker = true;
145
146 {
147 Json::Value& obj = nfts.append(o.getJson(JsonOptions::none));
148
149 // Pull out the components of the nft ID.
150 obj[sfFlags.jsonName] = nft::getFlags(nftokenID);
151 obj[sfIssuer.jsonName] = to_string(nft::getIssuer(nftokenID));
152 obj[sfNFTokenTaxon.jsonName] =
153 nft::toUInt32(nft::getTaxon(nftokenID));
154 obj[jss::nft_serial] = nft::getSerial(nftokenID);
155 if (std::uint16_t xferFee = {nft::getTransferFee(nftokenID)})
156 obj[sfTransferFee.jsonName] = xferFee;
157 }
158
159 if (++cnt == limit)
160 {
161 result[jss::limit] = limit;
162 result[jss::marker] = to_string(o.getFieldH256(sfNFTokenID));
163 return result;
164 }
165 }
166
167 if (auto npm = (*cp)[~sfNextPageMin])
168 cp = ledger->read(Keylet(ltNFTOKEN_PAGE, *npm));
169 else
170 cp = nullptr;
171 }
172
173 if (markerSet && !markerFound)
174 return RPC::invalid_field_error(jss::marker);
175
176 result[jss::account] = toBase58(accountID);
178 return result;
179}
180
183{
184 auto const& params = context.params;
185 if (!params.isMember(jss::account))
186 return RPC::missing_field_error(jss::account);
187
188 if (!params[jss::account].isString())
189 return RPC::invalid_field_error(jss::account);
190
192 auto result = RPC::lookupLedger(ledger, context);
193 if (ledger == nullptr)
194 return result;
195
196 auto const id = parseBase58<AccountID>(params[jss::account].asString());
197 if (!id)
198 {
200 return result;
201 }
202 auto const accountID{id.value()};
203
204 if (!ledger->exists(keylet::account(accountID)))
206
208
209 if (params.isMember(jss::deletion_blockers_only) &&
210 params[jss::deletion_blockers_only].asBool())
211 {
212 struct
213 {
215 LedgerEntryType type;
216 } static constexpr deletionBlockers[] = {
217 {jss::check, ltCHECK},
218 {jss::escrow, ltESCROW},
219 {jss::nft_page, ltNFTOKEN_PAGE},
220 {jss::payment_channel, ltPAYCHAN},
221 {jss::state, ltRIPPLE_STATE},
222 {jss::xchain_owned_claim_id, ltXCHAIN_OWNED_CLAIM_ID},
223 {jss::xchain_owned_create_account_claim_id,
224 ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID},
225 {jss::bridge, ltBRIDGE},
226 {jss::mpt_issuance, ltMPTOKEN_ISSUANCE},
227 {jss::mptoken, ltMPTOKEN},
228 {jss::permissioned_domain, ltPERMISSIONED_DOMAIN}};
229
230 typeFilter.emplace();
231 typeFilter->reserve(std::size(deletionBlockers));
232
233 for (auto [name, type] : deletionBlockers)
234 {
235 if (params.isMember(jss::type) && name != params[jss::type])
236 {
237 continue;
238 }
239
240 typeFilter->push_back(type);
241 }
242 }
243 else
244 {
245 auto [rpcStatus, type] = RPC::chooseLedgerEntryType(params);
246
248 return RPC::invalid_field_error(jss::type);
249
250 if (rpcStatus)
251 {
252 result.clear();
253 rpcStatus.inject(result);
254 return result;
255 }
256 else if (type != ltANY)
257 {
258 typeFilter = std::vector<LedgerEntryType>({type});
259 }
260 }
261
262 unsigned int limit;
263 if (auto err = readLimitField(limit, RPC::Tuning::accountObjects, context))
264 return *err;
265
266 uint256 dirIndex;
267 uint256 entryIndex;
268 if (params.isMember(jss::marker))
269 {
270 auto const& marker = params[jss::marker];
271 if (!marker.isString())
272 return RPC::expected_field_error(jss::marker, "string");
273
274 auto const& markerStr = marker.asString();
275 auto const& idx = markerStr.find(',');
276 if (idx == std::string::npos)
277 return RPC::invalid_field_error(jss::marker);
278
279 if (!dirIndex.parseHex(markerStr.substr(0, idx)))
280 return RPC::invalid_field_error(jss::marker);
281
282 if (!entryIndex.parseHex(markerStr.substr(idx + 1)))
283 return RPC::invalid_field_error(jss::marker);
284 }
285
287 *ledger,
288 accountID,
289 typeFilter,
290 dirIndex,
291 entryIndex,
292 limit,
293 result))
294 return RPC::invalid_field_error(jss::marker);
295
296 result[jss::account] = toBase58(accountID);
298 return result;
299}
300
301} // namespace ripple
Lightweight wrapper to tag static string.
Definition: json_value.h:61
Represents a JSON value.
Definition: json_value.h:147
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:891
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:502
bool isZero() const
Definition: base_uint.h:539
T emplace(T... args)
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
static LimitRange constexpr accountNFTokens
Limits for the account_nftokens command, in pages.
static LimitRange constexpr accountObjects
Limits for the account_objects command.
Json::Value invalid_field_error(std::string const &name)
Definition: ErrorCodes.h:315
bool isAccountObjectsValidType(LedgerEntryType const &type)
Check if the type is a valid filtering type for account_objects method.
Definition: RPCHelpers.cpp:982
void inject_error(error_code_i code, JsonValue &json)
Add or update the json update to reflect the error code.
Definition: ErrorCodes.h:223
std::pair< RPC::Status, LedgerEntryType > chooseLedgerEntryType(Json::Value const &params)
Definition: RPCHelpers.cpp:932
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.
Definition: RPCHelpers.cpp:154
Json::Value expected_field_error(std::string const &name, std::string const &type)
Definition: ErrorCodes.h:339
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.
Definition: RPCHelpers.cpp:623
Json::Value missing_field_error(std::string const &name)
Definition: ErrorCodes.h:273
Charge const feeMediumBurdenRPC
Keylet nftpage(Keylet const &k, uint256 const &token)
Definition: Indexes.cpp:395
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:160
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Definition: Indexes.cpp:379
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Definition: Indexes.cpp:387
std::uint16_t getTransferFee(uint256 const &id)
Definition: nft.h:68
std::uint16_t getFlags(uint256 const &id)
Definition: nft.h:60
std::uint32_t toUInt32(Taxon t)
Definition: nft.h:48
Taxon getTaxon(uint256 const &id)
Definition: nft.h:108
AccountID getIssuer(uint256 const &id)
Definition: nft.h:120
std::uint32_t getSerial(uint256 const &id)
Definition: nft.h:76
uint256 constexpr pageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:106
@ rpcACT_NOT_FOUND
Definition: ErrorCodes.h:70
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
Json::Value rpcError(int iError)
Definition: RPCErr.cpp:29
Json::Value doAccountObjects(RPC::JsonContext &context)
Json::Value doAccountNFTs(RPC::JsonContext &context)
General RPC command that can retrieve objects in the account root.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:54
@ ltANY
A special type, matching any ledger entry type.
Definition: LedgerFormats.h:78
T size(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
Resource::Charge & loadType
Definition: Context.h:43
Json::Value params
Definition: Context.h:64