rippled
NFTokenUtils.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2021 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 <ripple/app/tx/impl/details/NFTokenUtils.h>
21 #include <ripple/basics/algorithm.h>
22 #include <ripple/ledger/Directory.h>
23 #include <ripple/ledger/View.h>
24 #include <ripple/protocol/Feature.h>
25 #include <ripple/protocol/STAccount.h>
26 #include <ripple/protocol/STArray.h>
27 #include <ripple/protocol/TxFlags.h>
28 #include <ripple/protocol/nftPageMask.h>
29 #include <functional>
30 #include <memory>
31 
32 namespace ripple {
33 
34 namespace nft {
35 
37 locatePage(ReadView const& view, AccountID owner, uint256 const& id)
38 {
39  auto const first = keylet::nftpage(keylet::nftpage_min(owner), id);
40  auto const last = keylet::nftpage_max(owner);
41 
42  // This NFT can only be found in the first page with a key that's strictly
43  // greater than `first`, so look for that, up until the maximum possible
44  // page.
45  return view.read(Keylet(
47  view.succ(first.key, last.key.next()).value_or(last.key)));
48 }
49 
51 locatePage(ApplyView& view, AccountID owner, uint256 const& id)
52 {
53  auto const first = keylet::nftpage(keylet::nftpage_min(owner), id);
54  auto const last = keylet::nftpage_max(owner);
55 
56  // This NFT can only be found in the first page with a key that's strictly
57  // greater than `first`, so look for that, up until the maximum possible
58  // page.
59  return view.peek(Keylet(
61  view.succ(first.key, last.key.next()).value_or(last.key)));
62 }
63 
66  ApplyView& view,
67  AccountID const& owner,
68  uint256 const& id,
69  std::function<void(ApplyView&, AccountID const&)> const& createCallback)
70 {
71  auto const base = keylet::nftpage_min(owner);
72  auto const first = keylet::nftpage(base, id);
73  auto const last = keylet::nftpage_max(owner);
74 
75  // This NFT can only be found in the first page with a key that's strictly
76  // greater than `first`, so look for that, up until the maximum possible
77  // page.
78  auto cp = view.peek(Keylet(
80  view.succ(first.key, last.key.next()).value_or(last.key)));
81 
82  // A suitable page doesn't exist; we'll have to create one.
83  if (!cp)
84  {
85  STArray arr;
86  cp = std::make_shared<SLE>(last);
87  cp->setFieldArray(sfNFTokens, arr);
88  view.insert(cp);
89  createCallback(view, owner);
90  return cp;
91  }
92 
93  STArray narr = cp->getFieldArray(sfNFTokens);
94 
95  // The right page still has space: we're good.
96  if (narr.size() != dirMaxTokensPerPage)
97  return cp;
98 
99  // We need to split the page in two: the first half of the items in this
100  // page will go into the new page; the rest will stay with the existing
101  // page.
102  //
103  // Note we can't always split the page exactly in half. All equivalent
104  // NFTs must be kept on the same page. So when the page contains
105  // equivalent NFTs, the split may be lopsided in order to keep equivalent
106  // NFTs on the same page.
107  STArray carr;
108  {
109  // We prefer to keep equivalent NFTs on a page boundary. That gives
110  // any additional equivalent NFTs maximum room for expansion.
111  // Round up the boundary until there's a non-equivalent entry.
112  uint256 const cmp =
113  narr[(dirMaxTokensPerPage / 2) - 1].getFieldH256(sfNFTokenID) &
115 
116  // Note that the calls to find_if_not() and (later) find_if()
117  // rely on the fact that narr is kept in sorted order.
118  auto splitIter = std::find_if_not(
119  narr.begin() + (dirMaxTokensPerPage / 2),
120  narr.end(),
121  [&cmp](STObject const& obj) {
122  return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) == cmp;
123  });
124 
125  // If we get all the way from the middle to the end with only
126  // equivalent NFTokens then check the front of the page for a
127  // place to make the split.
128  if (splitIter == narr.end())
129  splitIter = std::find_if(
130  narr.begin(), narr.end(), [&cmp](STObject const& obj) {
131  return (obj.getFieldH256(sfNFTokenID) & nft::pageMask) ==
132  cmp;
133  });
134 
135  // There should be no circumstance when splitIter == end(), but if it
136  // were to happen we should bail out because something is confused.
137  if (splitIter == narr.end())
138  return nullptr;
139 
140  // If splitIter == begin(), then the entire page is filled with
141  // equivalent tokens. This requires special handling.
142  if (splitIter == narr.begin())
143  {
144  // Prior to fixNFTokenDirV1 we simply stopped.
145  if (!view.rules().enabled(fixNFTokenDirV1))
146  return nullptr;
147  else
148  {
149  auto const relation{(id & nft::pageMask) <=> cmp};
150  if (relation == 0)
151  {
152  // If the passed in id belongs exactly on this (full) page
153  // this account simply cannot store the NFT.
154  return nullptr;
155  }
156 
157  if (relation > 0)
158  {
159  // We need to leave the entire contents of this page in
160  // narr so carr stays empty. The new NFT will be
161  // inserted in carr. This keeps the NFTs that must be
162  // together all on their own page.
163  splitIter = narr.end();
164  }
165 
166  // If neither of those conditions apply then put all of
167  // narr into carr and produce an empty narr where the new NFT
168  // will be inserted. Leave the split at narr.begin().
169  }
170  }
171 
172  // Split narr at splitIter.
173  STArray newCarr(
174  std::make_move_iterator(splitIter),
175  std::make_move_iterator(narr.end()));
176  narr.erase(splitIter, narr.end());
177  std::swap(carr, newCarr);
178  }
179 
180  // Determine the ID for the page index. This decision is conditional on
181  // fixNFTokenDirV1 being enabled. But the condition for the decision
182  // is not possible unless fixNFTokenDirV1 is enabled.
183  //
184  // Note that we use uint256::next() because there's a subtlety in the way
185  // NFT pages are structured. The low 96-bits of NFT ID must be strictly
186  // less than the low 96-bits of the enclosing page's index. In order to
187  // accommodate that requirement we use an index one higher than the
188  // largest NFT in the page.
189  uint256 const tokenIDForNewPage = narr.size() == dirMaxTokensPerPage
190  ? narr[dirMaxTokensPerPage - 1].getFieldH256(sfNFTokenID).next()
191  : carr[0].getFieldH256(sfNFTokenID);
192 
193  auto np = std::make_shared<SLE>(keylet::nftpage(base, tokenIDForNewPage));
194  np->setFieldArray(sfNFTokens, narr);
195  np->setFieldH256(sfNextPageMin, cp->key());
196 
197  if (auto ppm = (*cp)[~sfPreviousPageMin])
198  {
199  np->setFieldH256(sfPreviousPageMin, *ppm);
200 
201  if (auto p3 = view.peek(Keylet(ltNFTOKEN_PAGE, *ppm)))
202  {
203  p3->setFieldH256(sfNextPageMin, np->key());
204  view.update(p3);
205  }
206  }
207 
208  view.insert(np);
209 
210  cp->setFieldArray(sfNFTokens, carr);
211  cp->setFieldH256(sfPreviousPageMin, np->key());
212  view.update(cp);
213 
214  createCallback(view, owner);
215 
216  // fixNFTokenDirV1 corrects a bug in the initial implementation that
217  // would put an NFT in the wrong page. The problem was caused by an
218  // off-by-one subtlety that the NFT can only be stored in the first page
219  // with a key that's strictly greater than `first`
220  if (!view.rules().enabled(fixNFTokenDirV1))
221  return (first.key <= np->key()) ? np : cp;
222 
223  return (first.key < np->key()) ? np : cp;
224 }
225 
226 bool
227 compareTokens(uint256 const& a, uint256 const& b)
228 {
229  // The sort of NFTokens needs to be fully deterministic, but the sort
230  // is weird because we sort on the low 96-bits first. But if the low
231  // 96-bits are identical we still need a fully deterministic sort.
232  // So we sort on the low 96-bits first. If those are equal we sort on
233  // the whole thing.
234  if (auto const lowBitsCmp{(a & nft::pageMask) <=> (b & nft::pageMask)};
235  lowBitsCmp != 0)
236  return lowBitsCmp < 0;
237 
238  return a < b;
239 }
240 
242 TER
244 {
245  assert(nft.isFieldPresent(sfNFTokenID));
246 
247  // First, we need to locate the page the NFT belongs to, creating it
248  // if necessary. This operation may fail if it is impossible to insert
249  // the NFT.
251  view,
252  owner,
253  nft[sfNFTokenID],
254  [](ApplyView& view, AccountID const& owner) {
256  view,
257  view.peek(keylet::account(owner)),
258  1,
259  beast::Journal{beast::Journal::getNullSink()});
260  });
261 
262  if (!page)
264 
265  {
266  auto arr = page->getFieldArray(sfNFTokens);
267  arr.push_back(std::move(nft));
268 
269  arr.sort([](STObject const& o1, STObject const& o2) {
270  return compareTokens(
272  });
273 
274  page->setFieldArray(sfNFTokens, arr);
275  }
276 
277  view.update(page);
278 
279  return tesSUCCESS;
280 }
281 
282 static bool
284  ApplyView& view,
285  std::shared_ptr<SLE> const& p1,
286  std::shared_ptr<SLE> const& p2)
287 {
288  if (p1->key() >= p2->key())
289  Throw<std::runtime_error>("mergePages: pages passed in out of order!");
290 
291  if ((*p1)[~sfNextPageMin] != p2->key())
292  Throw<std::runtime_error>("mergePages: next link broken!");
293 
294  if ((*p2)[~sfPreviousPageMin] != p1->key())
295  Throw<std::runtime_error>("mergePages: previous link broken!");
296 
297  auto const p1arr = p1->getFieldArray(sfNFTokens);
298  auto const p2arr = p2->getFieldArray(sfNFTokens);
299 
300  // Now check whether to merge the two pages; it only makes sense to do
301  // this it would mean that one of them can be deleted as a result of
302  // the merge.
303 
304  if (p1arr.size() + p2arr.size() > dirMaxTokensPerPage)
305  return false;
306 
307  STArray x(p1arr.size() + p2arr.size());
308 
309  std::merge(
310  p1arr.begin(),
311  p1arr.end(),
312  p2arr.begin(),
313  p2arr.end(),
315  [](STObject const& a, STObject const& b) {
316  return compareTokens(
317  a.getFieldH256(sfNFTokenID), b.getFieldH256(sfNFTokenID));
318  });
319 
320  p2->setFieldArray(sfNFTokens, x);
321 
322  // So, at this point we need to unlink "p1" (since we just emptied it) but
323  // we need to first relink the directory: if p1 has a previous page (p0),
324  // load it, point it to p2 and point p2 to it.
325 
326  p2->makeFieldAbsent(sfPreviousPageMin);
327 
328  if (auto const ppm = (*p1)[~sfPreviousPageMin])
329  {
330  auto p0 = view.peek(Keylet(ltNFTOKEN_PAGE, *ppm));
331 
332  if (!p0)
333  Throw<std::runtime_error>("mergePages: p0 can't be located!");
334 
335  p0->setFieldH256(sfNextPageMin, p2->key());
336  view.update(p0);
337 
338  p2->setFieldH256(sfPreviousPageMin, *ppm);
339  }
340 
341  view.update(p2);
342  view.erase(p1);
343 
344  return true;
345 }
346 
348 TER
349 removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID)
350 {
351  std::shared_ptr<SLE> page = locatePage(view, owner, nftokenID);
352 
353  // If the page couldn't be found, the given NFT isn't owned by this account
354  if (!page)
355  return tecNO_ENTRY;
356 
357  return removeToken(view, owner, nftokenID, std::move(page));
358 }
359 
361 TER
363  ApplyView& view,
364  AccountID const& owner,
365  uint256 const& nftokenID,
366  std::shared_ptr<SLE>&& curr)
367 {
368  // We found a page, but the given NFT may not be in it.
369  auto arr = curr->getFieldArray(sfNFTokens);
370 
371  {
372  auto x = std::find_if(
373  arr.begin(), arr.end(), [&nftokenID](STObject const& obj) {
374  return (obj[sfNFTokenID] == nftokenID);
375  });
376 
377  if (x == arr.end())
378  return tecNO_ENTRY;
379 
380  arr.erase(x);
381  }
382 
383  // Page management:
384  auto const loadPage = [&view](
385  std::shared_ptr<SLE> const& page1,
386  SF_UINT256 const& field) {
387  std::shared_ptr<SLE> page2;
388 
389  if (auto const id = (*page1)[~field])
390  {
391  page2 = view.peek(Keylet(ltNFTOKEN_PAGE, *id));
392 
393  if (!page2)
394  Throw<std::runtime_error>(
395  "page " + to_string(page1->key()) + " has a broken " +
396  field.getName() + " field pointing to " + to_string(*id));
397  }
398 
399  return page2;
400  };
401 
402  auto const prev = loadPage(curr, sfPreviousPageMin);
403  auto const next = loadPage(curr, sfNextPageMin);
404 
405  if (!arr.empty())
406  {
407  // The current page isn't empty. Update it and then try to consolidate
408  // pages. Note that this consolidation attempt may actually merge three
409  // pages into one!
410  curr->setFieldArray(sfNFTokens, arr);
411  view.update(curr);
412 
413  int cnt = 0;
414 
415  if (prev && mergePages(view, prev, curr))
416  cnt--;
417 
418  if (next && mergePages(view, curr, next))
419  cnt--;
420 
421  if (cnt != 0)
423  view,
424  view.peek(keylet::account(owner)),
425  cnt,
426  beast::Journal{beast::Journal::getNullSink()});
427 
428  return tesSUCCESS;
429  }
430 
431  // The page is empty, so we can just unlink it and then remove it.
432  if (prev)
433  {
434  // Make our previous page point to our next page:
435  if (next)
436  prev->setFieldH256(sfNextPageMin, next->key());
437  else
438  prev->makeFieldAbsent(sfNextPageMin);
439 
440  view.update(prev);
441  }
442 
443  if (next)
444  {
445  // Make our next page point to our previous page:
446  if (prev)
447  next->setFieldH256(sfPreviousPageMin, prev->key());
448  else
449  next->makeFieldAbsent(sfPreviousPageMin);
450 
451  view.update(next);
452  }
453 
454  view.erase(curr);
455 
456  int cnt = 1;
457 
458  // Since we're here, try to consolidate the previous and current pages
459  // of the page we removed (if any) into one. mergePages() _should_
460  // always return false. Since tokens are burned one at a time, there
461  // should never be a page containing one token sitting between two pages
462  // that have few enough tokens that they can be merged.
463  //
464  // But, in case that analysis is wrong, it's good to leave this code here
465  // just in case.
466  if (prev && next &&
467  mergePages(
468  view,
469  view.peek(Keylet(ltNFTOKEN_PAGE, prev->key())),
470  view.peek(Keylet(ltNFTOKEN_PAGE, next->key()))))
471  cnt++;
472 
474  view,
475  view.peek(keylet::account(owner)),
476  -1 * cnt,
477  beast::Journal{beast::Journal::getNullSink()});
478 
479  return tesSUCCESS;
480 }
481 
484  ReadView const& view,
485  AccountID const& owner,
486  uint256 const& nftokenID)
487 {
488  std::shared_ptr<SLE const> page = locatePage(view, owner, nftokenID);
489 
490  // If the page couldn't be found, the given NFT isn't owned by this account
491  if (!page)
492  return std::nullopt;
493 
494  // We found a candidate page, but the given NFT may not be in it.
495  for (auto const& t : page->getFieldArray(sfNFTokens))
496  {
497  if (t[sfNFTokenID] == nftokenID)
498  return t;
499  }
500 
501  return std::nullopt;
502 }
503 
506  ApplyView& view,
507  AccountID const& owner,
508  uint256 const& nftokenID)
509 {
510  std::shared_ptr<SLE> page = locatePage(view, owner, nftokenID);
511 
512  // If the page couldn't be found, the given NFT isn't owned by this account
513  if (!page)
514  return std::nullopt;
515 
516  // We found a candidate page, but the given NFT may not be in it.
517  for (auto const& t : page->getFieldArray(sfNFTokens))
518  {
519  if (t[sfNFTokenID] == nftokenID)
520  // This std::optional constructor is explicit, so it is spelled out.
522  std::in_place, t, std::move(page));
523  }
524  return std::nullopt;
525 }
526 
529  ApplyView& view,
530  Keylet const& directory,
531  std::size_t maxDeletableOffers)
532 {
533  if (maxDeletableOffers == 0)
534  return 0;
535 
536  std::optional<std::uint64_t> pageIndex{0};
537  std::size_t deletedOffersCount = 0;
538 
539  do
540  {
541  auto const page = view.peek(keylet::page(directory, *pageIndex));
542  if (!page)
543  break;
544 
545  // We get the index of the next page in case the current
546  // page is deleted after all of its entries have been removed
547  pageIndex = (*page)[~sfIndexNext];
548 
549  auto offerIndexes = page->getFieldV256(sfIndexes);
550 
551  // We reverse-iterate the offer directory page to delete all entries.
552  // Deleting an entry in a NFTokenOffer directory page won't cause
553  // entries from other pages to move to the current, so, it is safe to
554  // delete entries one by one in the page. It is required to iterate
555  // backwards to handle iterator invalidation for vector, as we are
556  // deleting during iteration.
557  for (int i = offerIndexes.size() - 1; i >= 0; --i)
558  {
559  if (auto const offer = view.peek(keylet::nftoffer(offerIndexes[i])))
560  {
561  if (deleteTokenOffer(view, offer))
562  ++deletedOffersCount;
563  else
564  Throw<std::runtime_error>(
565  "Offer " + to_string(offerIndexes[i]) +
566  " cannot be deleted!");
567  }
568 
569  if (maxDeletableOffers == deletedOffersCount)
570  break;
571  }
572  } while (pageIndex.value_or(0) && maxDeletableOffers != deletedOffersCount);
573 
574  return deletedOffersCount;
575 }
576 
577 TER
578 notTooManyOffers(ReadView const& view, uint256 const& nftokenID)
579 {
580  std::size_t totalOffers = 0;
581 
582  {
583  Dir buys(view, keylet::nft_buys(nftokenID));
584  for (auto iter = buys.begin(); iter != buys.end(); iter.next_page())
585  {
586  totalOffers += iter.page_size();
587  if (totalOffers > maxDeletableTokenOfferEntries)
588  return tefTOO_BIG;
589  }
590  }
591 
592  {
593  Dir sells(view, keylet::nft_sells(nftokenID));
594  for (auto iter = sells.begin(); iter != sells.end(); iter.next_page())
595  {
596  totalOffers += iter.page_size();
597  if (totalOffers > maxDeletableTokenOfferEntries)
598  return tefTOO_BIG;
599  }
600  }
601  return tesSUCCESS;
602 }
603 
604 bool
606 {
607  if (offer->getType() != ltNFTOKEN_OFFER)
608  return false;
609 
610  auto const owner = (*offer)[sfOwner];
611 
612  if (!view.dirRemove(
613  keylet::ownerDir(owner),
614  (*offer)[sfOwnerNode],
615  offer->key(),
616  false))
617  return false;
618 
619  auto const nftokenID = (*offer)[sfNFTokenID];
620 
621  if (!view.dirRemove(
622  ((*offer)[sfFlags] & tfSellNFToken) ? keylet::nft_sells(nftokenID)
623  : keylet::nft_buys(nftokenID),
624  (*offer)[sfNFTokenOfferNode],
625  offer->key(),
626  false))
627  return false;
628 
630  view,
631  view.peek(keylet::account(owner)),
632  -1,
633  beast::Journal{beast::Journal::getNullSink()});
634 
635  view.erase(offer);
636  return true;
637 }
638 
639 } // namespace nft
640 } // namespace ripple
ripple::STArray::size
size_type size() const
Definition: STArray.h:248
ripple::sfIndexNext
const SF_UINT64 sfIndexNext
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:303
std::in_place
T in_place
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
ripple::Dir::begin
const_iterator begin() const
Definition: Directory.cpp:34
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
std::shared_ptr
STL class.
ripple::sfOwnerNode
const SF_UINT64 sfOwnerNode
ripple::TypedField
A field with a type known at compile time.
Definition: SField.h:271
ripple::maxDeletableTokenOfferEntries
constexpr std::size_t maxDeletableTokenOfferEntries
The maximum number of offers in an offer directory for NFT to be burnable.
Definition: Protocol.h:70
ripple::ApplyView::peek
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
ripple::sfNFTokenID
const SF_UINT256 sfNFTokenID
functional
ripple::nft::findTokenAndPage
std::optional< TokenAndPage > findTokenAndPage(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
Definition: NFTokenUtils.cpp:505
ripple::nft::removeTokenOffersWithLimit
std::size_t removeTokenOffersWithLimit(ApplyView &view, Keylet const &directory, std::size_t maxDeletableOffers)
Delete up to a specified number of offers from the specified token offer directory.
Definition: NFTokenUtils.cpp:528
ripple::sfOwner
const SF_ACCOUNT sfOwner
ripple::ApplyView::erase
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
std::find_if_not
T find_if_not(T... args)
ripple::nft::mergePages
static bool mergePages(ApplyView &view, std::shared_ptr< SLE > const &p1, std::shared_ptr< SLE > const &p2)
Definition: NFTokenUtils.cpp:283
ripple::nft::notTooManyOffers
TER notTooManyOffers(ReadView const &view, uint256 const &nftokenID)
Returns tesSUCCESS if NFToken has few enough offers that it can be burned.
Definition: NFTokenUtils.cpp:578
std::back_inserter
T back_inserter(T... args)
ripple::keylet::nftoffer
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition: Indexes.cpp:355
ripple::dirMaxTokensPerPage
constexpr std::size_t dirMaxTokensPerPage
The maximum number of items in an NFT page.
Definition: Protocol.h:61
ripple::ApplyView::update
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
ripple::nft::findToken
std::optional< STObject > findToken(ReadView const &view, AccountID const &owner, uint256 const &nftokenID)
Finds the specified token in the owner's token directory.
Definition: NFTokenUtils.cpp:483
std::function
ripple::nft::removeToken
TER removeToken(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
Remove the token from the owner's token directory.
Definition: NFTokenUtils.cpp:349
ripple::sfNFTokenOfferNode
const SF_UINT64 sfNFTokenOfferNode
ripple::nft::compareTokens
bool compareTokens(uint256 const &a, uint256 const &b)
Definition: NFTokenUtils.cpp:227
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:134
ripple::nft::pageMask
constexpr uint256 pageMask(std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"))
ripple::sfIndexes
const SF_VECTOR256 sfIndexes
ripple::ApplyView::dirRemove
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
Definition: ApplyView.cpp:189
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:82
ripple::keylet::nftpage_min
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Definition: Indexes.cpp:332
ripple::adjustOwnerCount
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition: View.cpp:713
ripple::keylet::nftpage
Keylet nftpage(Keylet const &k, uint256 const &token)
Definition: Indexes.cpp:348
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:133
ripple::ltNFTOKEN_OFFER
@ ltNFTOKEN_OFFER
A ledger object which identifies an offer to buy or sell an NFT.
Definition: LedgerFormats.h:162
ripple::TERSubset< CanCvtToTER >
ripple::keylet::page
Keylet page(uint256 const &key, std::uint64_t index) noexcept
A page in a directory.
Definition: Indexes.cpp:309
ripple::STArray
Definition: STArray.h:28
ripple::keylet::nft_sells
Keylet nft_sells(uint256 const &id) noexcept
The directory of sell offers for the specified NFT.
Definition: Indexes.cpp:368
ripple::keylet::nftpage_max
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Definition: Indexes.cpp:340
ripple::tfSellNFToken
constexpr const std::uint32_t tfSellNFToken
Definition: TxFlags.h:152
std::merge
T merge(T... args)
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::keylet::nft_buys
Keylet nft_buys(uint256 const &id) noexcept
The directory of buy offers for the specified NFT.
Definition: Indexes.cpp:362
ripple::ReadView::succ
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.
ripple::fixNFTokenDirV1
const uint256 fixNFTokenDirV1
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
memory
std::swap
T swap(T... args)
ripple::STArray::begin
iterator begin()
Definition: STArray.h:224
ripple::sfNFTokens
const SField sfNFTokens
ripple::STObject
Definition: STObject.h:51
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:125
ripple::ltNFTOKEN_PAGE
@ ltNFTOKEN_PAGE
A ledger object which contains a list of NFTs.
Definition: LedgerFormats.h:156
ripple::ApplyView::insert
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::tefTOO_BIG
@ tefTOO_BIG
Definition: TER.h:166
ripple::tecNO_SUITABLE_NFTOKEN_PAGE
@ tecNO_SUITABLE_NFTOKEN_PAGE
Definition: TER.h:288
ripple::Dir
Definition: Directory.h:28
ripple::sfNextPageMin
const SF_UINT256 sfNextPageMin
std::optional
ripple::sfPreviousPageMin
const SF_UINT256 sfPreviousPageMin
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::STArray::erase
iterator erase(iterator pos)
Definition: STArray.h:290
ripple::Dir::const_iterator::next_page
const_iterator & next_page()
Definition: Directory.cpp:100
ripple::tecNO_ENTRY
@ tecNO_ENTRY
Definition: TER.h:273
ripple::Dir::end
const_iterator end() const
Definition: Directory.cpp:52
std::make_move_iterator
T make_move_iterator(T... args)
ripple::nft::locatePage
static std::shared_ptr< SLE const > locatePage(ReadView const &view, AccountID owner, uint256 const &id)
Definition: NFTokenUtils.cpp:37
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:222
ripple::nft::getPageForToken
static std::shared_ptr< SLE > getPageForToken(ApplyView &view, AccountID const &owner, uint256 const &id, std::function< void(ApplyView &, AccountID const &)> const &createCallback)
Definition: NFTokenUtils.cpp:65
ripple::nft::insertToken
TER insertToken(ApplyView &view, AccountID owner, STObject &&nft)
Insert the token in the owner's token directory.
Definition: NFTokenUtils.cpp:243
ripple::nft::deleteTokenOffer
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
Definition: NFTokenUtils.cpp:605
ripple::STArray::end
iterator end()
Definition: STArray.h:230
ripple::STObject::getFieldH256
uint256 getFieldH256(SField const &field) const
Definition: STObject.cpp:583