xrpld
Loading...
Searching...
No Matches
AccountNFTs.cpp
1#include <xrpld/rpc/Context.h>
2#include <xrpld/rpc/detail/RPCHelpers.h>
3#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
4#include <xrpld/rpc/detail/Tuning.h>
5
6#include <xrpl/basics/base_uint.h>
7#include <xrpl/json/json_value.h>
8#include <xrpl/ledger/ReadView.h>
9#include <xrpl/protocol/AccountID.h>
10#include <xrpl/protocol/ErrorCodes.h>
11#include <xrpl/protocol/Indexes.h>
12#include <xrpl/protocol/LedgerFormats.h>
13#include <xrpl/protocol/RPCErr.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/jss.h>
16#include <xrpl/protocol/nft.h>
17#include <xrpl/protocol/nftPageMask.h>
18#include <xrpl/resource/Fees.h>
19
20#include <cstdint>
21#include <memory>
22
23namespace xrpl {
24
35json::Value
37{
38 auto const& params = context.params;
39 if (!params.isMember(jss::account))
40 return RPC::missingFieldError(jss::account);
41
42 if (!params[jss::account].isString())
43 return RPC::invalidFieldError(jss::account);
44
45 auto id = parseBase58<AccountID>(params[jss::account].asString());
46 if (!id)
47 {
49 }
50
52 auto result = RPC::lookupLedger(ledger, context);
53 if (ledger == nullptr)
54 return result;
55 auto const accountID{id.value()};
56
57 if (!ledger->exists(keylet::account(accountID)))
59
60 unsigned int limit = 0;
61 if (auto err = readLimitField(limit, RPC::Tuning::kAccountNfTokens, context))
62 return *err;
63
64 uint256 marker;
65 bool const markerSet = params.isMember(jss::marker);
66
67 if (markerSet)
68 {
69 auto const& m = params[jss::marker];
70 if (!m.isString())
71 return RPC::expectedFieldError(jss::marker, "string");
72
73 if (!marker.parseHex(m.asString()))
74 return RPC::invalidFieldError(jss::marker);
75 }
76
77 auto const first = keylet::nftokenPage(keylet::nftokenPageMin(accountID), marker);
78 auto const last = keylet::nftokenPageMax(accountID);
79
80 auto cp = ledger->read(
81 Keylet(ltNFTOKEN_PAGE, ledger->succ(first.key, last.key.next()).value_or(last.key)));
82
83 std::uint32_t cnt = 0;
84 auto& nfts = (result[jss::account_nfts] = json::ValueType::Array);
85
86 // Continue iteration from the current page:
87 bool pastMarker = marker.isZero();
88 bool markerFound = false;
89 uint256 const maskedMarker = marker & nft::kPageMask;
90 while (cp)
91 {
92 auto arr = cp->getFieldArray(sfNFTokens);
93
94 for (auto const& o : arr)
95 {
96 // Scrolling past the marker gets weird. We need to look at
97 // a couple of conditions.
98 //
99 // 1. If the low 96-bits don't match, then we compare only
100 // against the low 96-bits, since that's what determines
101 // the sort order of the pages.
102 //
103 // 2. However, within one page there can be a number of
104 // NFTokenIDs that all have the same low 96 bits. If we're
105 // in that case then we need to compare against the full
106 // 256 bits.
107 uint256 const nftokenID = o[sfNFTokenID];
108 uint256 const maskedNftokenID = nftokenID & nft::kPageMask;
109
110 if (!pastMarker)
111 {
112 if (maskedNftokenID < maskedMarker)
113 continue;
114
115 if (maskedNftokenID == maskedMarker && nftokenID < marker)
116 continue;
117
118 if (nftokenID == marker)
119 {
120 markerFound = true;
121 continue;
122 }
123 }
124
125 if (markerSet && !markerFound)
126 return RPC::invalidFieldError(jss::marker);
127
128 pastMarker = true;
129
130 {
131 json::Value& obj = nfts.append(o.getJson(JsonOptions::Values::None));
132
133 // Pull out the components of the nft ID.
134 obj[sfFlags.jsonName] = nft::getFlags(nftokenID);
135 obj[sfIssuer.jsonName] = to_string(nft::getIssuer(nftokenID));
136 obj[sfNFTokenTaxon.jsonName] = nft::toUInt32(nft::getTaxon(nftokenID));
137 obj[jss::nft_serial] = nft::getSequence(nftokenID);
138 if (std::uint16_t const xferFee = {nft::getTransferFee(nftokenID)})
139 obj[sfTransferFee.jsonName] = xferFee;
140 }
141
142 if (++cnt == limit)
143 {
144 result[jss::limit] = limit;
145 result[jss::marker] = to_string(o.getFieldH256(sfNFTokenID));
146 return result;
147 }
148 }
149
150 if (auto npm = (*cp)[~sfNextPageMin])
151 {
152 cp = ledger->read(Keylet(ltNFTOKEN_PAGE, *npm));
153 }
154 else
155 {
156 cp = nullptr;
157 }
158 }
159
160 if (markerSet && !markerFound)
161 return RPC::invalidFieldError(jss::marker);
162
163 result[jss::account] = toBase58(accountID);
165 return result;
166}
167
168} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
bool isZero() const
Definition base_uint.h:544
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:507
@ Array
array value (ordered list)
Definition json_value.h:25
static constexpr LimitRange kAccountNfTokens
Limits for the account_nftokens command, in pages.
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext const &context, json::Value &result)
Looks up a ledger from a request and fills a json::Value with ledger data.
json::Value invalidFieldError(std::string const &name)
Definition ErrorCodes.h:273
json::Value missingFieldError(std::string const &name)
Definition ErrorCodes.h:231
json::Value expectedFieldError(std::string const &name, std::string const &type)
Definition ErrorCodes.h:297
Charge const kFeeMediumBurdenRpc
Keylet nftokenPageMin(AccountID const &owner)
NFT page keylets.
Definition Indexes.cpp:384
Keylet nftokenPage(Keylet const &k, uint256 const &token)
Definition Indexes.cpp:400
Keylet nftokenPageMax(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Definition Indexes.cpp:392
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
std::uint32_t toUInt32(Taxon t)
Definition nft.h:27
Taxon getTaxon(uint256 const &id)
Definition nft.h:87
constexpr uint256 kPageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
std::uint32_t getSequence(uint256 const &id)
Definition nft.h:55
AccountID getIssuer(uint256 const &id)
Definition nft.h:99
std::uint16_t getTransferFee(uint256 const &id)
Definition nft.h:47
std::uint16_t getFlags(uint256 const &id)
Definition nft.h:39
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ RpcActNotFound
Definition ErrorCodes.h:52
@ RpcActMalformed
Definition ErrorCodes.h:72
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:93
json::Value doAccountNFTs(RPC::JsonContext &context)
General RPC command that can retrieve objects in the account root.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
BaseUInt< 256 > uint256
Definition base_uint.h:562
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:19
Resource::Charge & loadType
Definition Context.h:22
json::Value params
Definition Context.h:43