rippled
View.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 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/basics/Log.h>
21 #include <ripple/basics/chrono.h>
22 #include <ripple/basics/contract.h>
23 #include <ripple/ledger/ReadView.h>
24 #include <ripple/ledger/View.h>
25 #include <ripple/protocol/Feature.h>
26 #include <ripple/protocol/Protocol.h>
27 #include <ripple/protocol/Quality.h>
28 #include <ripple/protocol/st.h>
29 #include <cassert>
30 #include <optional>
31 
32 namespace ripple {
33 
34 namespace detail {
35 
36 template <
37  class V,
38  class N,
39  class = std::enable_if_t<
40  std::is_same_v<std::remove_cv_t<N>, SLE> &&
41  std::is_base_of_v<ReadView, V>>>
42 bool
44  V& view,
45  uint256 const& root,
46  std::shared_ptr<N>& page,
47  unsigned int& index,
48  uint256& entry)
49 {
50  auto const& svIndexes = page->getFieldV256(sfIndexes);
51  assert(index <= svIndexes.size());
52 
53  if (index >= svIndexes.size())
54  {
55  auto const next = page->getFieldU64(sfIndexNext);
56 
57  if (!next)
58  {
59  entry.zero();
60  return false;
61  }
62 
63  if constexpr (std::is_const_v<N>)
64  page = view.read(keylet::page(root, next));
65  else
66  page = view.peek(keylet::page(root, next));
67 
68  assert(page);
69 
70  if (!page)
71  return false;
72 
73  index = 0;
74 
75  return internalDirNext(view, root, page, index, entry);
76  }
77 
78  entry = svIndexes[index++];
79  return true;
80 }
81 
82 template <
83  class V,
84  class N,
85  class = std::enable_if_t<
86  std::is_same_v<std::remove_cv_t<N>, SLE> &&
87  std::is_base_of_v<ReadView, V>>>
88 bool
90  V& view,
91  uint256 const& root,
92  std::shared_ptr<N>& page,
93  unsigned int& index,
94  uint256& entry)
95 {
96  if constexpr (std::is_const_v<N>)
97  page = view.read(keylet::page(root));
98  else
99  page = view.peek(keylet::page(root));
100 
101  if (!page)
102  return false;
103 
104  index = 0;
105 
106  return internalDirNext(view, root, page, index, entry);
107 }
108 
109 } // namespace detail
110 
111 bool
113  ApplyView& view,
114  uint256 const& root,
115  std::shared_ptr<SLE>& page,
116  unsigned int& index,
117  uint256& entry)
118 {
119  return detail::internalDirFirst(view, root, page, index, entry);
120 }
121 
122 bool
124  ApplyView& view,
125  uint256 const& root,
126  std::shared_ptr<SLE>& page,
127  unsigned int& index,
128  uint256& entry)
129 {
130  return detail::internalDirNext(view, root, page, index, entry);
131 }
132 
133 bool
135  ReadView const& view,
136  uint256 const& root,
138  unsigned int& index,
139  uint256& entry)
140 {
141  return detail::internalDirFirst(view, root, page, index, entry);
142 }
143 
144 bool
146  ReadView const& view,
147  uint256 const& root,
149  unsigned int& index,
150  uint256& entry)
151 {
152  return detail::internalDirNext(view, root, page, index, entry);
153 }
154 
155 //------------------------------------------------------------------------------
156 //
157 // Observers
158 //
159 //------------------------------------------------------------------------------
160 
161 bool
163 {
164  using d = NetClock::duration;
165  using tp = NetClock::time_point;
166 
167  return exp && (view.parentCloseTime() >= tp{d{*exp}});
168 }
169 
170 bool
171 isGlobalFrozen(ReadView const& view, AccountID const& issuer)
172 {
173  if (isXRP(issuer))
174  return false;
175  if (auto const sle = view.read(keylet::account(issuer)))
176  return sle->isFlag(lsfGlobalFreeze);
177  return false;
178 }
179 
180 bool
182  ReadView const& view,
183  AccountID const& account,
184  Currency const& currency,
185  AccountID const& issuer)
186 {
187  if (isXRP(currency))
188  return false;
189  if (issuer != account)
190  {
191  // Check if the issuer froze the line
192  auto const sle = view.read(keylet::line(account, issuer, currency));
193  if (sle &&
194  sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
195  return true;
196  }
197  return false;
198 }
199 
200 // Can the specified account spend the specified currency issued by
201 // the specified issuer or does the freeze flag prohibit it?
202 bool
204  ReadView const& view,
205  AccountID const& account,
206  Currency const& currency,
207  AccountID const& issuer)
208 {
209  if (isXRP(currency))
210  return false;
211  auto sle = view.read(keylet::account(issuer));
212  if (sle && sle->isFlag(lsfGlobalFreeze))
213  return true;
214  if (issuer != account)
215  {
216  // Check if the issuer froze the line
217  sle = view.read(keylet::line(account, issuer, currency));
218  if (sle &&
219  sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
220  return true;
221  }
222  return false;
223 }
224 
225 STAmount
227  ReadView const& view,
228  AccountID const& account,
229  Currency const& currency,
230  AccountID const& issuer,
231  FreezeHandling zeroIfFrozen,
232  beast::Journal j)
233 {
234  STAmount amount;
235  if (isXRP(currency))
236  {
237  return {xrpLiquid(view, account, 0, j)};
238  }
239 
240  // IOU: Return balance on trust line modulo freeze
241  auto const sle = view.read(keylet::line(account, issuer, currency));
242  if (!sle)
243  {
244  amount.clear({currency, issuer});
245  }
246  else if (
247  (zeroIfFrozen == fhZERO_IF_FROZEN) &&
248  isFrozen(view, account, currency, issuer))
249  {
250  amount.clear(Issue(currency, issuer));
251  }
252  else
253  {
254  amount = sle->getFieldAmount(sfBalance);
255  if (account > issuer)
256  {
257  // Put balance in account terms.
258  amount.negate();
259  }
260  amount.setIssuer(issuer);
261  }
262  JLOG(j.trace()) << "accountHolds:"
263  << " account=" << to_string(account)
264  << " amount=" << amount.getFullText();
265 
266  return view.balanceHook(account, issuer, amount);
267 }
268 
269 STAmount
271  ReadView const& view,
272  AccountID const& account,
273  Issue const& issue,
274  FreezeHandling zeroIfFrozen,
275  beast::Journal j)
276 {
277  return accountHolds(
278  view, account, issue.currency, issue.account, zeroIfFrozen, j);
279 }
280 
281 STAmount
283  ReadView const& view,
284  AccountID const& id,
285  STAmount const& saDefault,
286  FreezeHandling freezeHandling,
287  beast::Journal j)
288 {
289  if (!saDefault.native() && saDefault.getIssuer() == id)
290  return saDefault;
291 
292  return accountHolds(
293  view,
294  id,
295  saDefault.getCurrency(),
296  saDefault.getIssuer(),
297  freezeHandling,
298  j);
299 }
300 
301 // Prevent ownerCount from wrapping under error conditions.
302 //
303 // adjustment allows the ownerCount to be adjusted up or down in multiple steps.
304 // If id != std::nullopt, then do error reporting.
305 //
306 // Returns adjusted owner count.
307 static std::uint32_t
309  std::uint32_t current,
310  std::int32_t adjustment,
311  std::optional<AccountID> const& id = std::nullopt,
313 {
314  std::uint32_t adjusted{current + adjustment};
315  if (adjustment > 0)
316  {
317  // Overflow is well defined on unsigned
318  if (adjusted < current)
319  {
320  if (id)
321  {
322  JLOG(j.fatal())
323  << "Account " << *id << " owner count exceeds max!";
324  }
326  }
327  }
328  else
329  {
330  // Underflow is well defined on unsigned
331  if (adjusted > current)
332  {
333  if (id)
334  {
335  JLOG(j.fatal())
336  << "Account " << *id << " owner count set below 0!";
337  }
338  adjusted = 0;
339  assert(!id);
340  }
341  }
342  return adjusted;
343 }
344 
345 XRPAmount
347  ReadView const& view,
348  AccountID const& id,
349  std::int32_t ownerCountAdj,
350  beast::Journal j)
351 {
352  auto const sle = view.read(keylet::account(id));
353  if (sle == nullptr)
354  return beast::zero;
355 
356  // Return balance minus reserve
357  std::uint32_t const ownerCount = confineOwnerCount(
358  view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
359 
360  // AMMs have no reserve requirement
361  auto const reserve = sle->isFieldPresent(sfAMMID)
362  ? XRPAmount{0}
363  : view.fees().accountReserve(ownerCount);
364 
365  auto const fullBalance = sle->getFieldAmount(sfBalance);
366 
367  auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
368 
369  STAmount const amount =
370  (balance < reserve) ? STAmount{0} : balance - reserve;
371 
372  JLOG(j.trace()) << "accountHolds:"
373  << " account=" << to_string(id)
374  << " amount=" << amount.getFullText()
375  << " fullBalance=" << fullBalance.getFullText()
376  << " balance=" << balance.getFullText()
377  << " reserve=" << reserve << " ownerCount=" << ownerCount
378  << " ownerCountAdj=" << ownerCountAdj;
379 
380  return amount.xrp();
381 }
382 
383 void
385  ReadView const& view,
386  Keylet const& root,
387  std::function<void(std::shared_ptr<SLE const> const&)> const& f)
388 {
389  assert(root.type == ltDIR_NODE);
390 
391  if (root.type != ltDIR_NODE)
392  return;
393 
394  auto pos = root;
395 
396  while (true)
397  {
398  auto sle = view.read(pos);
399  if (!sle)
400  return;
401  for (auto const& key : sle->getFieldV256(sfIndexes))
402  f(view.read(keylet::child(key)));
403  auto const next = sle->getFieldU64(sfIndexNext);
404  if (!next)
405  return;
406  pos = keylet::page(root, next);
407  }
408 }
409 
410 bool
412  ReadView const& view,
413  Keylet const& root,
414  uint256 const& after,
415  std::uint64_t const hint,
416  unsigned int limit,
417  std::function<bool(std::shared_ptr<SLE const> const&)> const& f)
418 {
419  assert(root.type == ltDIR_NODE);
420 
421  if (root.type != ltDIR_NODE)
422  return false;
423 
424  auto currentIndex = root;
425 
426  // If startAfter is not zero try jumping to that page using the hint
427  if (after.isNonZero())
428  {
429  auto const hintIndex = keylet::page(root, hint);
430 
431  if (auto hintDir = view.read(hintIndex))
432  {
433  for (auto const& key : hintDir->getFieldV256(sfIndexes))
434  {
435  if (key == after)
436  {
437  // We found the hint, we can start here
438  currentIndex = hintIndex;
439  break;
440  }
441  }
442  }
443 
444  bool found = false;
445  for (;;)
446  {
447  auto const ownerDir = view.read(currentIndex);
448  if (!ownerDir)
449  return found;
450  for (auto const& key : ownerDir->getFieldV256(sfIndexes))
451  {
452  if (!found)
453  {
454  if (key == after)
455  found = true;
456  }
457  else if (f(view.read(keylet::child(key))) && limit-- <= 1)
458  {
459  return found;
460  }
461  }
462 
463  auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
464  if (uNodeNext == 0)
465  return found;
466  currentIndex = keylet::page(root, uNodeNext);
467  }
468  }
469  else
470  {
471  for (;;)
472  {
473  auto const ownerDir = view.read(currentIndex);
474  if (!ownerDir)
475  return true;
476  for (auto const& key : ownerDir->getFieldV256(sfIndexes))
477  if (f(view.read(keylet::child(key))) && limit-- <= 1)
478  return true;
479  auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
480  if (uNodeNext == 0)
481  return true;
482  currentIndex = keylet::page(root, uNodeNext);
483  }
484  }
485 }
486 
487 Rate
488 transferRate(ReadView const& view, AccountID const& issuer)
489 {
490  auto const sle = view.read(keylet::account(issuer));
491 
492  if (sle && sle->isFieldPresent(sfTransferRate))
493  return Rate{sle->getFieldU32(sfTransferRate)};
494 
495  return parityRate;
496 }
497 
498 bool
500  ReadView const& validLedger,
501  ReadView const& testLedger,
503  const char* reason)
504 {
505  bool ret = true;
506 
507  if (validLedger.info().seq < testLedger.info().seq)
508  {
509  // valid -> ... -> test
510  auto hash = hashOfSeq(
511  testLedger,
512  validLedger.info().seq,
513  beast::Journal{beast::Journal::getNullSink()});
514  if (hash && (*hash != validLedger.info().hash))
515  {
516  JLOG(s) << reason << " incompatible with valid ledger";
517 
518  JLOG(s) << "Hash(VSeq): " << to_string(*hash);
519 
520  ret = false;
521  }
522  }
523  else if (validLedger.info().seq > testLedger.info().seq)
524  {
525  // test -> ... -> valid
526  auto hash = hashOfSeq(
527  validLedger,
528  testLedger.info().seq,
529  beast::Journal{beast::Journal::getNullSink()});
530  if (hash && (*hash != testLedger.info().hash))
531  {
532  JLOG(s) << reason << " incompatible preceding ledger";
533 
534  JLOG(s) << "Hash(NSeq): " << to_string(*hash);
535 
536  ret = false;
537  }
538  }
539  else if (
540  (validLedger.info().seq == testLedger.info().seq) &&
541  (validLedger.info().hash != testLedger.info().hash))
542  {
543  // Same sequence number, different hash
544  JLOG(s) << reason << " incompatible ledger";
545 
546  ret = false;
547  }
548 
549  if (!ret)
550  {
551  JLOG(s) << "Val: " << validLedger.info().seq << " "
552  << to_string(validLedger.info().hash);
553 
554  JLOG(s) << "New: " << testLedger.info().seq << " "
555  << to_string(testLedger.info().hash);
556  }
557 
558  return ret;
559 }
560 
561 bool
563  uint256 const& validHash,
564  LedgerIndex validIndex,
565  ReadView const& testLedger,
567  const char* reason)
568 {
569  bool ret = true;
570 
571  if (testLedger.info().seq > validIndex)
572  {
573  // Ledger we are testing follows last valid ledger
574  auto hash = hashOfSeq(
575  testLedger,
576  validIndex,
578  if (hash && (*hash != validHash))
579  {
580  JLOG(s) << reason << " incompatible following ledger";
581  JLOG(s) << "Hash(VSeq): " << to_string(*hash);
582 
583  ret = false;
584  }
585  }
586  else if (
587  (validIndex == testLedger.info().seq) &&
588  (testLedger.info().hash != validHash))
589  {
590  JLOG(s) << reason << " incompatible ledger";
591 
592  ret = false;
593  }
594 
595  if (!ret)
596  {
597  JLOG(s) << "Val: " << validIndex << " " << to_string(validHash);
598 
599  JLOG(s) << "New: " << testLedger.info().seq << " "
600  << to_string(testLedger.info().hash);
601  }
602 
603  return ret;
604 }
605 
606 bool
607 dirIsEmpty(ReadView const& view, Keylet const& k)
608 {
609  auto const sleNode = view.read(k);
610  if (!sleNode)
611  return true;
612  if (!sleNode->getFieldV256(sfIndexes).empty())
613  return false;
614  // The first page of a directory may legitimately be empty even if there
615  // are other pages (the first page is the anchor page) so check to see if
616  // there is another page. If there is, the directory isn't empty.
617  return sleNode->getFieldU64(sfIndexNext) == 0;
618 }
619 
622 {
623  std::set<uint256> amendments;
624 
625  if (auto const sle = view.read(keylet::amendments()))
626  {
627  if (sle->isFieldPresent(sfAmendments))
628  {
629  auto const& v = sle->getFieldV256(sfAmendments);
630  amendments.insert(v.begin(), v.end());
631  }
632  }
633 
634  return amendments;
635 }
636 
639 {
641 
642  if (auto const sle = view.read(keylet::amendments()))
643  {
644  if (sle->isFieldPresent(sfMajorities))
645  {
646  using tp = NetClock::time_point;
647  using d = tp::duration;
648 
649  auto const majorities = sle->getFieldArray(sfMajorities);
650 
651  for (auto const& m : majorities)
652  ret[m.getFieldH256(sfAmendment)] =
653  tp(d(m.getFieldU32(sfCloseTime)));
654  }
655  }
656 
657  return ret;
658 }
659 
661 hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
662 {
663  // Easy cases...
664  if (seq > ledger.seq())
665  {
666  JLOG(journal.warn())
667  << "Can't get seq " << seq << " from " << ledger.seq() << " future";
668  return std::nullopt;
669  }
670  if (seq == ledger.seq())
671  return ledger.info().hash;
672  if (seq == (ledger.seq() - 1))
673  return ledger.info().parentHash;
674 
675  if (int diff = ledger.seq() - seq; diff <= 256)
676  {
677  // Within 256...
678  auto const hashIndex = ledger.read(keylet::skip());
679  if (hashIndex)
680  {
681  assert(
682  hashIndex->getFieldU32(sfLastLedgerSequence) ==
683  (ledger.seq() - 1));
684  STVector256 vec = hashIndex->getFieldV256(sfHashes);
685  if (vec.size() >= diff)
686  return vec[vec.size() - diff];
687  JLOG(journal.warn())
688  << "Ledger " << ledger.seq() << " missing hash for " << seq
689  << " (" << vec.size() << "," << diff << ")";
690  }
691  else
692  {
693  JLOG(journal.warn())
694  << "Ledger " << ledger.seq() << ":" << ledger.info().hash
695  << " missing normal list";
696  }
697  }
698 
699  if ((seq & 0xff) != 0)
700  {
701  JLOG(journal.debug())
702  << "Can't get seq " << seq << " from " << ledger.seq() << " past";
703  return std::nullopt;
704  }
705 
706  // in skiplist
707  auto const hashIndex = ledger.read(keylet::skip(seq));
708  if (hashIndex)
709  {
710  auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
711  assert(lastSeq >= seq);
712  assert((lastSeq & 0xff) == 0);
713  auto const diff = (lastSeq - seq) >> 8;
714  STVector256 vec = hashIndex->getFieldV256(sfHashes);
715  if (vec.size() > diff)
716  return vec[vec.size() - diff - 1];
717  }
718  JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq()
719  << " error";
720  return std::nullopt;
721 }
722 
723 //------------------------------------------------------------------------------
724 //
725 // Modifiers
726 //
727 //------------------------------------------------------------------------------
728 
729 void
731  ApplyView& view,
732  std::shared_ptr<SLE> const& sle,
733  std::int32_t amount,
734  beast::Journal j)
735 {
736  if (!sle)
737  return;
738  assert(amount != 0);
740  AccountID const id = (*sle)[sfAccount];
741  std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
742  view.adjustOwnerCountHook(id, current, adjusted);
743  sle->setFieldU32(sfOwnerCount, adjusted);
744  view.update(sle);
745 }
746 
747 std::function<void(SLE::ref)>
749 {
750  return [&account](std::shared_ptr<SLE> const& sle) {
751  (*sle)[sfOwner] = account;
752  };
753 }
754 
755 TER
757  ApplyView& view,
758  const bool bSrcHigh,
759  AccountID const& uSrcAccountID,
760  AccountID const& uDstAccountID,
761  uint256 const& uIndex, // --> ripple state entry
762  SLE::ref sleAccount, // --> the account being set.
763  const bool bAuth, // --> authorize account.
764  const bool bNoRipple, // --> others cannot ripple through
765  const bool bFreeze, // --> funds cannot leave
766  STAmount const& saBalance, // --> balance of account being set.
767  // Issuer should be noAccount()
768  STAmount const& saLimit, // --> limit for account being set.
769  // Issuer should be the account being set.
770  std::uint32_t uQualityIn,
771  std::uint32_t uQualityOut,
772  beast::Journal j)
773 {
774  JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", "
775  << to_string(uDstAccountID) << ", "
776  << saBalance.getFullText();
777 
778  auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
779  auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
780 
781  auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
782  view.insert(sleRippleState);
783 
784  auto lowNode = view.dirInsert(
785  keylet::ownerDir(uLowAccountID),
786  sleRippleState->key(),
787  describeOwnerDir(uLowAccountID));
788 
789  if (!lowNode)
790  return tecDIR_FULL;
791 
792  auto highNode = view.dirInsert(
793  keylet::ownerDir(uHighAccountID),
794  sleRippleState->key(),
795  describeOwnerDir(uHighAccountID));
796 
797  if (!highNode)
798  return tecDIR_FULL;
799 
800  const bool bSetDst = saLimit.getIssuer() == uDstAccountID;
801  const bool bSetHigh = bSrcHigh ^ bSetDst;
802 
803  assert(sleAccount);
804  if (!sleAccount)
805  return tefINTERNAL;
806 
807  assert(
808  sleAccount->getAccountID(sfAccount) ==
809  (bSetHigh ? uHighAccountID : uLowAccountID));
810  auto const slePeer =
811  view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
812  if (!slePeer)
813  return tecNO_TARGET;
814 
815  // Remember deletion hints.
816  sleRippleState->setFieldU64(sfLowNode, *lowNode);
817  sleRippleState->setFieldU64(sfHighNode, *highNode);
818 
819  sleRippleState->setFieldAmount(
820  bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
821  sleRippleState->setFieldAmount(
822  bSetHigh ? sfLowLimit : sfHighLimit,
823  STAmount(
824  {saBalance.getCurrency(),
825  bSetDst ? uSrcAccountID : uDstAccountID}));
826 
827  if (uQualityIn)
828  sleRippleState->setFieldU32(
829  bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
830 
831  if (uQualityOut)
832  sleRippleState->setFieldU32(
833  bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
834 
835  std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
836 
837  if (bAuth)
838  {
839  uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth);
840  }
841  if (bNoRipple)
842  {
843  uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple);
844  }
845  if (bFreeze)
846  {
847  uFlags |= (!bSetHigh ? lsfLowFreeze : lsfHighFreeze);
848  }
849 
850  if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
851  {
852  // The other side's default is no rippling
853  uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
854  }
855 
856  sleRippleState->setFieldU32(sfFlags, uFlags);
857  adjustOwnerCount(view, sleAccount, 1, j);
858 
859  // ONLY: Create ripple balance.
860  sleRippleState->setFieldAmount(
861  sfBalance, bSetHigh ? -saBalance : saBalance);
862 
863  view.creditHook(
864  uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
865 
866  return tesSUCCESS;
867 }
868 
869 TER
871  ApplyView& view,
872  std::shared_ptr<SLE> const& sleRippleState,
873  AccountID const& uLowAccountID,
874  AccountID const& uHighAccountID,
875  beast::Journal j)
876 {
877  // Detect legacy dirs.
878  std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode);
879  std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode);
880 
881  JLOG(j.trace()) << "trustDelete: Deleting ripple line: low";
882 
883  if (!view.dirRemove(
884  keylet::ownerDir(uLowAccountID),
885  uLowNode,
886  sleRippleState->key(),
887  false))
888  {
889  return tefBAD_LEDGER;
890  }
891 
892  JLOG(j.trace()) << "trustDelete: Deleting ripple line: high";
893 
894  if (!view.dirRemove(
895  keylet::ownerDir(uHighAccountID),
896  uHighNode,
897  sleRippleState->key(),
898  false))
899  {
900  return tefBAD_LEDGER;
901  }
902 
903  JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
904  view.erase(sleRippleState);
905 
906  return tesSUCCESS;
907 }
908 
909 TER
911 {
912  if (!sle)
913  return tesSUCCESS;
914  auto offerIndex = sle->key();
915  auto owner = sle->getAccountID(sfAccount);
916 
917  // Detect legacy directories.
918  uint256 uDirectory = sle->getFieldH256(sfBookDirectory);
919 
920  if (!view.dirRemove(
921  keylet::ownerDir(owner),
922  sle->getFieldU64(sfOwnerNode),
923  offerIndex,
924  false))
925  {
926  return tefBAD_LEDGER;
927  }
928 
929  if (!view.dirRemove(
930  keylet::page(uDirectory),
931  sle->getFieldU64(sfBookNode),
932  offerIndex,
933  false))
934  {
935  return tefBAD_LEDGER;
936  }
937 
938  adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
939 
940  view.erase(sle);
941 
942  return tesSUCCESS;
943 }
944 
945 // Direct send w/o fees:
946 // - Redeeming IOUs and/or sending sender's own IOUs.
947 // - Create trust line if needed.
948 // --> bCheckIssuer : normally require issuer to be involved.
949 TER
951  ApplyView& view,
952  AccountID const& uSenderID,
953  AccountID const& uReceiverID,
954  STAmount const& saAmount,
955  bool bCheckIssuer,
956  beast::Journal j)
957 {
958  AccountID const& issuer = saAmount.getIssuer();
959  Currency const& currency = saAmount.getCurrency();
960 
961  // Make sure issuer is involved.
962  assert(!bCheckIssuer || uSenderID == issuer || uReceiverID == issuer);
963  (void)issuer;
964 
965  // Disallow sending to self.
966  assert(uSenderID != uReceiverID);
967 
968  bool const bSenderHigh = uSenderID > uReceiverID;
969  auto const index = keylet::line(uSenderID, uReceiverID, currency);
970 
971  assert(!isXRP(uSenderID) && uSenderID != noAccount());
972  assert(!isXRP(uReceiverID) && uReceiverID != noAccount());
973 
974  // If the line exists, modify it accordingly.
975  if (auto const sleRippleState = view.peek(index))
976  {
977  STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
978 
979  if (bSenderHigh)
980  saBalance.negate(); // Put balance in sender terms.
981 
982  view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
983 
984  STAmount const saBefore = saBalance;
985 
986  saBalance -= saAmount;
987 
988  JLOG(j.trace()) << "rippleCredit: " << to_string(uSenderID) << " -> "
989  << to_string(uReceiverID)
990  << " : before=" << saBefore.getFullText()
991  << " amount=" << saAmount.getFullText()
992  << " after=" << saBalance.getFullText();
993 
994  std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
995  bool bDelete = false;
996 
997  // FIXME This NEEDS to be cleaned up and simplified. It's impossible
998  // for anyone to understand.
999  if (saBefore > beast::zero
1000  // Sender balance was positive.
1001  && saBalance <= beast::zero
1002  // Sender is zero or negative.
1003  && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
1004  // Sender reserve is set.
1005  &&
1006  static_cast<bool>(
1007  uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
1008  static_cast<bool>(
1009  view.read(keylet::account(uSenderID))->getFlags() &
1010  lsfDefaultRipple) &&
1011  !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
1012  !sleRippleState->getFieldAmount(
1013  !bSenderHigh ? sfLowLimit : sfHighLimit)
1014  // Sender trust limit is 0.
1015  && !sleRippleState->getFieldU32(
1016  !bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
1017  // Sender quality in is 0.
1018  && !sleRippleState->getFieldU32(
1019  !bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
1020  // Sender quality out is 0.
1021  {
1022  // Clear the reserve of the sender, possibly delete the line!
1024  view, view.peek(keylet::account(uSenderID)), -1, j);
1025 
1026  // Clear reserve flag.
1027  sleRippleState->setFieldU32(
1028  sfFlags,
1029  uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
1030 
1031  // Balance is zero, receiver reserve is clear.
1032  bDelete = !saBalance // Balance is zero.
1033  && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
1034  // Receiver reserve is clear.
1035  }
1036 
1037  if (bSenderHigh)
1038  saBalance.negate();
1039 
1040  // Want to reflect balance to zero even if we are deleting line.
1041  sleRippleState->setFieldAmount(sfBalance, saBalance);
1042  // ONLY: Adjust ripple balance.
1043 
1044  if (bDelete)
1045  {
1046  return trustDelete(
1047  view,
1048  sleRippleState,
1049  bSenderHigh ? uReceiverID : uSenderID,
1050  !bSenderHigh ? uReceiverID : uSenderID,
1051  j);
1052  }
1053 
1054  view.update(sleRippleState);
1055  return tesSUCCESS;
1056  }
1057 
1058  STAmount const saReceiverLimit({currency, uReceiverID});
1059  STAmount saBalance{saAmount};
1060 
1061  saBalance.setIssuer(noAccount());
1062 
1063  JLOG(j.debug()) << "rippleCredit: "
1064  "create line: "
1065  << to_string(uSenderID) << " -> " << to_string(uReceiverID)
1066  << " : " << saAmount.getFullText();
1067 
1068  auto const sleAccount = view.peek(keylet::account(uReceiverID));
1069  if (!sleAccount)
1070  return tefINTERNAL;
1071 
1072  bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
1073 
1074  return trustCreate(
1075  view,
1076  bSenderHigh,
1077  uSenderID,
1078  uReceiverID,
1079  index.key,
1080  sleAccount,
1081  false,
1082  noRipple,
1083  false,
1084  saBalance,
1085  saReceiverLimit,
1086  0,
1087  0,
1088  j);
1089 }
1090 
1091 // Send regardless of limits.
1092 // --> saAmount: Amount/currency/issuer to deliver to receiver.
1093 // <-- saActual: Amount actually cost. Sender pays fees.
1094 static TER
1096  ApplyView& view,
1097  AccountID const& uSenderID,
1098  AccountID const& uReceiverID,
1099  STAmount const& saAmount,
1100  STAmount& saActual,
1101  beast::Journal j,
1102  WaiveTransferFee waiveFee)
1103 {
1104  auto const issuer = saAmount.getIssuer();
1105 
1106  assert(!isXRP(uSenderID) && !isXRP(uReceiverID));
1107  assert(uSenderID != uReceiverID);
1108 
1109  if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
1110  {
1111  // Direct send: redeeming IOUs and/or sending own IOUs.
1112  auto const ter =
1113  rippleCredit(view, uSenderID, uReceiverID, saAmount, false, j);
1114  if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS)
1115  return ter;
1116  saActual = saAmount;
1117  return tesSUCCESS;
1118  }
1119 
1120  // Sending 3rd party IOUs: transit.
1121 
1122  // Calculate the amount to transfer accounting
1123  // for any transfer fees if the fee is not waived:
1124  saActual = (waiveFee == WaiveTransferFee::Yes)
1125  ? saAmount
1126  : multiply(saAmount, transferRate(view, issuer));
1127 
1128  JLOG(j.debug()) << "rippleSend> " << to_string(uSenderID) << " - > "
1129  << to_string(uReceiverID)
1130  << " : deliver=" << saAmount.getFullText()
1131  << " cost=" << saActual.getFullText();
1132 
1133  TER terResult = rippleCredit(view, issuer, uReceiverID, saAmount, true, j);
1134 
1135  if (tesSUCCESS == terResult)
1136  terResult = rippleCredit(view, uSenderID, issuer, saActual, true, j);
1137 
1138  return terResult;
1139 }
1140 
1141 TER
1143  ApplyView& view,
1144  AccountID const& uSenderID,
1145  AccountID const& uReceiverID,
1146  STAmount const& saAmount,
1147  beast::Journal j,
1148  WaiveTransferFee waiveFee)
1149 {
1150  assert(saAmount >= beast::zero);
1151 
1152  /* If we aren't sending anything or if the sender is the same as the
1153  * receiver then we don't need to do anything.
1154  */
1155  if (!saAmount || (uSenderID == uReceiverID))
1156  return tesSUCCESS;
1157 
1158  if (!saAmount.native())
1159  {
1160  STAmount saActual;
1161 
1162  JLOG(j.trace()) << "accountSend: " << to_string(uSenderID) << " -> "
1163  << to_string(uReceiverID) << " : "
1164  << saAmount.getFullText();
1165 
1166  return rippleSend(
1167  view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1168  }
1169 
1170  /* XRP send which does not check reserve and can do pure adjustment.
1171  * Note that sender or receiver may be null and this not a mistake; this
1172  * setup is used during pathfinding and it is carefully controlled to
1173  * ensure that transfers are balanced.
1174  */
1175  TER terResult(tesSUCCESS);
1176 
1177  SLE::pointer sender = uSenderID != beast::zero
1178  ? view.peek(keylet::account(uSenderID))
1179  : SLE::pointer();
1180  SLE::pointer receiver = uReceiverID != beast::zero
1181  ? view.peek(keylet::account(uReceiverID))
1182  : SLE::pointer();
1183 
1184  if (auto stream = j.trace())
1185  {
1186  std::string sender_bal("-");
1187  std::string receiver_bal("-");
1188 
1189  if (sender)
1190  sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1191 
1192  if (receiver)
1193  receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
1194 
1195  stream << "accountSend> " << to_string(uSenderID) << " (" << sender_bal
1196  << ") -> " << to_string(uReceiverID) << " (" << receiver_bal
1197  << ") : " << saAmount.getFullText();
1198  }
1199 
1200  if (sender)
1201  {
1202  if (sender->getFieldAmount(sfBalance) < saAmount)
1203  {
1204  // VFALCO Its laborious to have to mutate the
1205  // TER based on params everywhere
1206  terResult = view.open() ? TER{telFAILED_PROCESSING}
1208  }
1209  else
1210  {
1211  auto const sndBal = sender->getFieldAmount(sfBalance);
1212  view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
1213 
1214  // Decrement XRP balance.
1215  sender->setFieldAmount(sfBalance, sndBal - saAmount);
1216  view.update(sender);
1217  }
1218  }
1219 
1220  if (tesSUCCESS == terResult && receiver)
1221  {
1222  // Increment XRP balance.
1223  auto const rcvBal = receiver->getFieldAmount(sfBalance);
1224  receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
1225  view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
1226 
1227  view.update(receiver);
1228  }
1229 
1230  if (auto stream = j.trace())
1231  {
1232  std::string sender_bal("-");
1233  std::string receiver_bal("-");
1234 
1235  if (sender)
1236  sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1237 
1238  if (receiver)
1239  receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
1240 
1241  stream << "accountSend< " << to_string(uSenderID) << " (" << sender_bal
1242  << ") -> " << to_string(uReceiverID) << " (" << receiver_bal
1243  << ") : " << saAmount.getFullText();
1244  }
1245 
1246  return terResult;
1247 }
1248 
1249 static bool
1251  ApplyView& view,
1252  SLE::pointer state,
1253  bool bSenderHigh,
1254  AccountID const& sender,
1255  STAmount const& before,
1256  STAmount const& after,
1257  beast::Journal j)
1258 {
1259  if (!state)
1260  return false;
1261  std::uint32_t const flags(state->getFieldU32(sfFlags));
1262 
1263  auto sle = view.peek(keylet::account(sender));
1264  if (!sle)
1265  return false;
1266 
1267  // YYY Could skip this if rippling in reverse.
1268  if (before > beast::zero
1269  // Sender balance was positive.
1270  && after <= beast::zero
1271  // Sender is zero or negative.
1272  && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
1273  // Sender reserve is set.
1274  && static_cast<bool>(
1275  flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
1276  static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
1277  !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
1278  !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
1279  // Sender trust limit is 0.
1280  && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
1281  // Sender quality in is 0.
1282  &&
1283  !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
1284  // Sender quality out is 0.
1285  {
1286  // VFALCO Where is the line being deleted?
1287  // Clear the reserve of the sender, possibly delete the line!
1288  adjustOwnerCount(view, sle, -1, j);
1289 
1290  // Clear reserve flag.
1291  state->setFieldU32(
1292  sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
1293 
1294  // Balance is zero, receiver reserve is clear.
1295  if (!after // Balance is zero.
1296  && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
1297  return true;
1298  }
1299  return false;
1300 }
1301 
1302 TER
1304  ApplyView& view,
1305  AccountID const& account,
1306  STAmount const& amount,
1307  Issue const& issue,
1308  beast::Journal j)
1309 {
1310  assert(!isXRP(account) && !isXRP(issue.account));
1311 
1312  // Consistency check
1313  assert(issue == amount.issue());
1314 
1315  // Can't send to self!
1316  assert(issue.account != account);
1317 
1318  JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": "
1319  << amount.getFullText();
1320 
1321  bool bSenderHigh = issue.account > account;
1322 
1323  auto const index = keylet::line(issue.account, account, issue.currency);
1324 
1325  if (auto state = view.peek(index))
1326  {
1327  STAmount final_balance = state->getFieldAmount(sfBalance);
1328 
1329  if (bSenderHigh)
1330  final_balance.negate(); // Put balance in sender terms.
1331 
1332  STAmount const start_balance = final_balance;
1333 
1334  final_balance -= amount;
1335 
1336  auto const must_delete = updateTrustLine(
1337  view,
1338  state,
1339  bSenderHigh,
1340  issue.account,
1341  start_balance,
1342  final_balance,
1343  j);
1344 
1345  view.creditHook(issue.account, account, amount, start_balance);
1346 
1347  if (bSenderHigh)
1348  final_balance.negate();
1349 
1350  // Adjust the balance on the trust line if necessary. We do this even if
1351  // we are going to delete the line to reflect the correct balance at the
1352  // time of deletion.
1353  state->setFieldAmount(sfBalance, final_balance);
1354  if (must_delete)
1355  return trustDelete(
1356  view,
1357  state,
1358  bSenderHigh ? account : issue.account,
1359  bSenderHigh ? issue.account : account,
1360  j);
1361 
1362  view.update(state);
1363 
1364  return tesSUCCESS;
1365  }
1366 
1367  // NIKB TODO: The limit uses the receiver's account as the issuer and
1368  // this is unnecessarily inefficient as copying which could be avoided
1369  // is now required. Consider available options.
1370  STAmount const limit({issue.currency, account});
1371  STAmount final_balance = amount;
1372 
1373  final_balance.setIssuer(noAccount());
1374 
1375  auto const receiverAccount = view.peek(keylet::account(account));
1376  if (!receiverAccount)
1377  return tefINTERNAL;
1378 
1379  bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0;
1380 
1381  return trustCreate(
1382  view,
1383  bSenderHigh,
1384  issue.account,
1385  account,
1386  index.key,
1387  receiverAccount,
1388  false,
1389  noRipple,
1390  false,
1391  final_balance,
1392  limit,
1393  0,
1394  0,
1395  j);
1396 }
1397 
1398 TER
1400  ApplyView& view,
1401  AccountID const& account,
1402  STAmount const& amount,
1403  Issue const& issue,
1404  beast::Journal j)
1405 {
1406  assert(!isXRP(account) && !isXRP(issue.account));
1407 
1408  // Consistency check
1409  assert(issue == amount.issue());
1410 
1411  // Can't send to self!
1412  assert(issue.account != account);
1413 
1414  JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": "
1415  << amount.getFullText();
1416 
1417  bool bSenderHigh = account > issue.account;
1418 
1419  if (auto state =
1420  view.peek(keylet::line(account, issue.account, issue.currency)))
1421  {
1422  STAmount final_balance = state->getFieldAmount(sfBalance);
1423 
1424  if (bSenderHigh)
1425  final_balance.negate(); // Put balance in sender terms.
1426 
1427  STAmount const start_balance = final_balance;
1428 
1429  final_balance -= amount;
1430 
1431  auto const must_delete = updateTrustLine(
1432  view, state, bSenderHigh, account, start_balance, final_balance, j);
1433 
1434  view.creditHook(account, issue.account, amount, start_balance);
1435 
1436  if (bSenderHigh)
1437  final_balance.negate();
1438 
1439  // Adjust the balance on the trust line if necessary. We do this even if
1440  // we are going to delete the line to reflect the correct balance at the
1441  // time of deletion.
1442  state->setFieldAmount(sfBalance, final_balance);
1443 
1444  if (must_delete)
1445  {
1446  return trustDelete(
1447  view,
1448  state,
1449  bSenderHigh ? issue.account : account,
1450  bSenderHigh ? account : issue.account,
1451  j);
1452  }
1453 
1454  view.update(state);
1455  return tesSUCCESS;
1456  }
1457 
1458  // In order to hold an IOU, a trust line *MUST* exist to track the
1459  // balance. If it doesn't, then something is very wrong. Don't try
1460  // to continue.
1461  JLOG(j.fatal()) << "redeemIOU: " << to_string(account)
1462  << " attempts to redeem " << amount.getFullText()
1463  << " but no trust line exists!";
1464 
1465  return tefINTERNAL;
1466 }
1467 
1468 TER
1470  ApplyView& view,
1471  AccountID const& from,
1472  AccountID const& to,
1473  STAmount const& amount,
1474  beast::Journal j)
1475 {
1476  assert(from != beast::zero);
1477  assert(to != beast::zero);
1478  assert(from != to);
1479  assert(amount.native());
1480 
1481  SLE::pointer const sender = view.peek(keylet::account(from));
1482  SLE::pointer const receiver = view.peek(keylet::account(to));
1483  if (!sender || !receiver)
1484  return tefINTERNAL;
1485 
1486  JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> "
1487  << to_string(to) << ") : " << amount.getFullText();
1488 
1489  if (sender->getFieldAmount(sfBalance) < amount)
1490  {
1491  // VFALCO Its unfortunate we have to keep
1492  // mutating these TER everywhere
1493  // FIXME: this logic should be moved to callers maybe?
1494  return view.open() ? TER{telFAILED_PROCESSING}
1496  }
1497 
1498  // Decrement XRP balance.
1499  sender->setFieldAmount(
1500  sfBalance, sender->getFieldAmount(sfBalance) - amount);
1501  view.update(sender);
1502 
1503  receiver->setFieldAmount(
1504  sfBalance, receiver->getFieldAmount(sfBalance) + amount);
1505  view.update(receiver);
1506 
1507  return tesSUCCESS;
1508 }
1509 
1510 TER
1511 requireAuth(ReadView const& view, Issue const& issue, AccountID const& account)
1512 {
1513  if (isXRP(issue) || issue.account == account)
1514  return tesSUCCESS;
1515  if (auto const issuerAccount = view.read(keylet::account(issue.account));
1516  issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth)
1517  {
1518  if (auto const trustLine =
1519  view.read(keylet::line(account, issue.account, issue.currency)))
1520  return ((*trustLine)[sfFlags] &
1521  ((account > issue.account) ? lsfLowAuth : lsfHighAuth))
1522  ? tesSUCCESS
1523  : TER{tecNO_AUTH};
1524  return TER{tecNO_LINE};
1525  }
1526 
1527  return tesSUCCESS;
1528 }
1529 
1530 TER
1532  ApplyView& view,
1533  Keylet const& ownerDirKeylet,
1534  EntryDeleter const& deleter,
1535  beast::Journal j,
1536  std::optional<uint16_t> maxNodesToDelete)
1537 {
1538  // Delete all the entries in the account directory.
1539  std::shared_ptr<SLE> sleDirNode{};
1540  unsigned int uDirEntry{0};
1541  uint256 dirEntry{beast::zero};
1542  std::uint32_t deleted = 0;
1543 
1544  if (view.exists(ownerDirKeylet) &&
1545  dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
1546  {
1547  do
1548  {
1549  if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
1550  return tecINCOMPLETE;
1551 
1552  // Choose the right way to delete each directory node.
1553  auto sleItem = view.peek(keylet::child(dirEntry));
1554  if (!sleItem)
1555  {
1556  // Directory node has an invalid index. Bail out.
1557  JLOG(j.fatal())
1558  << "DeleteAccount: Directory node in ledger " << view.seq()
1559  << " has index to object that is missing: "
1560  << to_string(dirEntry);
1561  return tefBAD_LEDGER;
1562  }
1563 
1564  LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
1565  sleItem->getFieldU16(sfLedgerEntryType))};
1566 
1567  // Deleter handles the details of specific account-owned object
1568  // deletion
1569  auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
1570  if (ter != tesSUCCESS)
1571  return ter;
1572 
1573  // dirFirst() and dirNext() are like iterators with exposed
1574  // internal state. We'll take advantage of that exposed state
1575  // to solve a common C++ problem: iterator invalidation while
1576  // deleting elements from a container.
1577  //
1578  // We have just deleted one directory entry, which means our
1579  // "iterator state" is invalid.
1580  //
1581  // 1. During the process of getting an entry from the
1582  // directory uDirEntry was incremented from 'it' to 'it'+1.
1583  //
1584  // 2. We then deleted the entry at index 'it', which means the
1585  // entry that was at 'it'+1 has now moved to 'it'.
1586  //
1587  // 3. So we verify that uDirEntry is indeed 'it'+1. Then we jam it
1588  // back to 'it' to "un-invalidate" the iterator.
1589  assert(uDirEntry >= 1);
1590  if (uDirEntry == 0)
1591  {
1592  JLOG(j.error())
1593  << "DeleteAccount iterator re-validation failed.";
1594  return tefBAD_LEDGER;
1595  }
1596  if (skipEntry == SkipEntry::No)
1597  uDirEntry--;
1598 
1599  } while (
1600  dirNext(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
1601  }
1602 
1603  return tesSUCCESS;
1604 }
1605 
1606 TER
1608  ApplyView& view,
1609  std::shared_ptr<SLE> sleState,
1611  beast::Journal j)
1612 {
1613  if (!sleState || sleState->getType() != ltRIPPLE_STATE)
1614  return tecINTERNAL;
1615 
1616  auto const& [low, high] = std::minmax(
1617  sleState->getFieldAmount(sfLowLimit).getIssuer(),
1618  sleState->getFieldAmount(sfHighLimit).getIssuer());
1619  auto sleLow = view.peek(keylet::account(low));
1620  auto sleHigh = view.peek(keylet::account(high));
1621  if (!sleLow || !sleHigh)
1622  return tecINTERNAL;
1623  bool const ammLow = sleLow->isFieldPresent(sfAMMID);
1624  bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
1625 
1626  // can't both be AMM
1627  if (ammLow && ammHigh)
1628  return tecINTERNAL;
1629 
1630  // at least one must be
1631  if (!ammLow && !ammHigh)
1632  return terNO_AMM;
1633 
1634  // one must be the target amm
1635  if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
1636  return terNO_AMM;
1637 
1638  if (auto const ter = trustDelete(view, sleState, low, high, j);
1639  ter != tesSUCCESS)
1640  {
1641  JLOG(j.error())
1642  << "deleteAMMTrustLine: failed to delete the trustline.";
1643  return ter;
1644  }
1645 
1646  auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
1647  if (!(sleState->getFlags() & uFlags))
1648  return tecINTERNAL;
1649 
1650  adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
1651 
1652  return tesSUCCESS;
1653 }
1654 
1655 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:338
ripple::ReadView::info
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
ripple::rippleCredit
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition: View.cpp:950
ripple::sfIndexNext
const SF_UINT64 sfIndexNext
ripple::sfHighQualityIn
const SF_UINT32 sfHighQualityIn
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:312
ripple::sfOwnerCount
const SF_UINT32 sfOwnerCount
ripple::transferRate
Rate transferRate(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:488
ripple::cdirNext
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition: View.cpp:145
ripple::dirNext
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:123
ripple::STAmount::negate
void negate()
Definition: STAmount.h:405
ripple::STLedgerEntry::key
uint256 const & key() const
Returns the 'key' (or 'index') of this item.
Definition: STLedgerEntry.h:113
ripple::getMajorityAmendments
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:638
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
ripple::tecNO_TARGET
@ tecNO_TARGET
Definition: TER.h:285
ripple::getEnabledAmendments
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition: View.cpp:621
ripple::STLedgerEntry
Definition: STLedgerEntry.h:30
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::tefINTERNAL
@ tefINTERNAL
Definition: TER.h:167
ripple::lsfGlobalFreeze
@ lsfGlobalFreeze
Definition: LedgerFormats.h:262
std::string
STL class.
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::keylet::amendments
Keylet const & amendments() noexcept
The index of the amendment table.
Definition: Indexes.cpp:172
ripple::fhZERO_IF_FROZEN
@ fhZERO_IF_FROZEN
Definition: View.h:79
ripple::forEachItemAfter
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
Definition: View.cpp:411
ripple::sfOwnerNode
const SF_UINT64 sfOwnerNode
ripple::Rate
Represents a transfer rate.
Definition: Rate.h:37
ripple::accountSend
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1142
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:308
ripple::ApplyView::peek
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
ripple::STAmount::clear
void clear()
Definition: STAmount.h:412
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:350
ripple::describeOwnerDir
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition: View.cpp:748
ripple::lsfLowReserve
@ lsfLowReserve
Definition: LedgerFormats.h:286
ripple::STObject::getFieldU64
std::uint64_t getFieldU64(SField const &field) const
Definition: STObject.cpp:565
ripple::WaiveTransferFee::Yes
@ Yes
ripple::WaiveTransferFee
WaiveTransferFee
Definition: View.h:45
ripple::sfOwner
const SF_ACCOUNT sfOwner
ripple::detail::internalDirNext
bool internalDirNext(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:43
ripple::lsfLowAuth
@ lsfLowAuth
Definition: LedgerFormats.h:288
ripple::lsfLowNoRipple
@ lsfLowNoRipple
Definition: LedgerFormats.h:290
ripple::ApplyView::erase
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
ripple::sfBookDirectory
const SF_UINT256 sfBookDirectory
ripple::SLE
STLedgerEntry SLE
Definition: Application.h:66
ripple::ReadView::fees
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
ripple::accountHolds
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:226
ripple::LedgerHeader::parentHash
uint256 parentHash
Definition: LedgerHeader.h:52
ripple::ApplyView::creditHook
virtual void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
Definition: ApplyView.h:235
ripple::Issue::currency
Currency currency
Definition: Issue.h:38
ripple::LedgerHeader::seq
LedgerIndex seq
Definition: LedgerHeader.h:41
ripple::keylet::skip
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition: Indexes.cpp:154
beast::Journal::warn
Stream warn() const
Definition: Journal.h:326
ripple::keylet::child
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition: Indexes.cpp:148
ripple::ApplyView::update
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
ripple::hasExpired
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition: View.cpp:162
ripple::ReadView::balanceHook
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition: ReadView.h:182
ripple::deleteAMMTrustLine
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition: View.cpp:1607
ripple::sfCloseTime
const SF_UINT32 sfCloseTime
std::function
ripple::STAmount::setIssuer
void setIssuer(AccountID const &uIssuer)
Definition: STAmount.h:436
ripple::ReadView::parentCloseTime
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition: ReadView.h:115
ripple::FreezeHandling
FreezeHandling
Controls the treatment of frozen account balances.
Definition: View.h:79
ripple::tecINCOMPLETE
@ tecINCOMPLETE
Definition: TER.h:316
beast::Journal::getNullSink
static Sink & getNullSink()
Returns a Sink which does nothing.
Definition: beast_Journal.cpp:72
ripple::parityRate
const Rate parityRate(QUALITY_ONE)
A transfer rate signifying a 1:1 exchange.
Definition: Rate.h:101
ripple::ammAccountID
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
Definition: AMMCore.cpp:30
ripple::STAmount::xrp
XRPAmount xrp() const
Definition: STAmount.cpp:345
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:134
ripple::requireAuth
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account requires authorization.
Definition: View.cpp:1511
ripple::lsfHighAuth
@ lsfHighAuth
Definition: LedgerFormats.h:289
ripple::ltDIR_NODE
@ ltDIR_NODE
A ledger object which contains a list of object identifiers.
Definition: LedgerFormats.h:66
ripple::STAmount::getIssuer
AccountID const & getIssuer() const
Definition: STAmount.h:362
ripple::featureDeletableAccounts
const uint256 featureDeletableAccounts
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::LedgerHeader::hash
uint256 hash
Definition: LedgerHeader.h:49
ripple::sfLowNode
const SF_UINT64 sfLowNode
ripple::Keylet::key
uint256 key
Definition: Keylet.h:40
ripple::base_uint< 256 >
ripple::sfLowQualityOut
const SF_UINT32 sfLowQualityOut
ripple::STAmount::getFullText
std::string getFullText() const override
Definition: STAmount.cpp:559
ripple::sfLowLimit
const SF_AMOUNT sfLowLimit
ripple::isGlobalFrozen
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:171
ripple::lsfRequireAuth
@ lsfRequireAuth
Definition: LedgerFormats.h:257
ripple::tefBAD_LEDGER
@ tefBAD_LEDGER
Definition: TER.h:164
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:730
ripple::telFAILED_PROCESSING
@ telFAILED_PROCESSING
Definition: TER.h:56
ripple::lsfDefaultRipple
@ lsfDefaultRipple
Definition: LedgerFormats.h:263
ripple::majorityAmendments_t
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition: View.h:223
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:142
std::enable_if_t
ripple::STObject::setFieldAmount
void setFieldAmount(SField const &field, STAmount const &)
Definition: STObject.cpp:707
ripple::offerDelete
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:910
std::minmax
T minmax(T... args)
ripple::STObject::getAccountID
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:589
ripple::TERSubset< CanCvtToTER >
ripple::keylet::page
Keylet page(uint256 const &key, std::uint64_t index) noexcept
A page in a directory.
Definition: Indexes.cpp:318
ripple::tecFAILED_PROCESSING
@ tecFAILED_PROCESSING
Definition: TER.h:267
ripple::sfBookNode
const SF_UINT64 sfBookNode
ripple::sfLowQualityIn
const SF_UINT32 sfLowQualityIn
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:608
ripple::STAmount
Definition: STAmount.h:46
ripple::xrpAccount
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:168
beast::Journal::Stream
Provide a light-weight way to check active() before string formatting.
Definition: Journal.h:193
beast::Journal::error
Stream error() const
Definition: Journal.h:332
ripple::ReadView::exists
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:291
ripple::xrpLiquid
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:346
ripple::STObject::getFlags
std::uint32_t getFlags() const
Definition: STObject.cpp:481
ripple::hashOfSeq
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition: View.cpp:661
ripple::sfTransferRate
const SF_UINT32 sfTransferRate
ripple::STVector256::size
std::size_t size() const
Definition: STVector256.h:166
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
ripple::ValStatus::current
@ current
This was a new validation and was added.
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
ripple::sfHighLimit
const SF_AMOUNT sfHighLimit
ripple::keylet::line
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:202
std::map
STL class.
ripple::ApplyView::adjustOwnerCountHook
virtual void adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next)
Definition: ApplyView.h:246
ripple::trustDelete
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition: View.cpp:870
ripple::sfAMMID
const SF_UINT256 sfAMMID
ripple::STLedgerEntry::getType
LedgerEntryType getType() const
Definition: STLedgerEntry.h:119
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::cleanupOnAccountDelete
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< uint16_t > maxNodesToDelete)
Definition: View.cpp:1531
ripple::areCompatible
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, const char *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition: View.cpp:499
ripple::accountFunds
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:282
ripple::transferXRP
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition: View.cpp:1469
ripple::lsfHighNoRipple
@ lsfHighNoRipple
Definition: LedgerFormats.h:291
ripple::tecDIR_FULL
@ tecDIR_FULL
Definition: TER.h:268
ripple::multiply
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
ripple::lsfHighFreeze
@ lsfHighFreeze
Definition: LedgerFormats.h:293
ripple::sfHashes
const SF_VECTOR256 sfHashes
ripple::issueIOU
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1303
ripple::STAmount::native
bool native() const noexcept
Definition: STAmount.h:332
ripple::dirIsEmpty
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition: View.cpp:607
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:54
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::dirFirst
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:112
ripple::isIndividualFrozen
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:181
ripple::trustCreate
TER trustCreate(ApplyView &view, const bool bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, const bool bAuth, const bool bNoRipple, const bool bFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uQualityIn, std::uint32_t uQualityOut, beast::Journal j)
Create a trust line.
Definition: View.cpp:756
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::LedgerEntryType
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:53
ripple::tecNO_LINE
@ tecNO_LINE
Definition: TER.h:282
ripple::Fees::accountReserve
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Definition: protocol/Fees.h:49
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:122
ripple::base_uint::zero
void zero()
Definition: base_uint.h:542
ripple::redeemIOU
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1399
ripple::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::terNO_AMM
@ terNO_AMM
Definition: TER.h:220
cassert
ripple::ReadView::ownerCountHook
virtual std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const
Definition: ReadView.h:196
ripple::SkipEntry::No
@ No
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::STVector256
Definition: STVector256.h:29
ripple::detail::internalDirFirst
bool internalDirFirst(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:89
ripple::confineOwnerCount
static std::uint32_t confineOwnerCount(std::uint32_t current, std::int32_t adjustment, std::optional< AccountID > const &id=std::nullopt, beast::Journal j=beast::Journal{beast::Journal::getNullSink()})
Definition: View.cpp:308
ripple::sfHighQualityOut
const SF_UINT32 sfHighQualityOut
optional
ripple::forEachItem
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
Definition: View.cpp:384
beast::Journal::debug
Stream debug() const
Definition: Journal.h:314
ripple::after
static bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: Escrow.cpp:88
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::sfAccount
const SF_ACCOUNT sfAccount
ripple::ltRIPPLE_STATE
@ ltRIPPLE_STATE
A ledger object which describes a bidirectional trust line.
Definition: LedgerFormats.h:74
ripple::lsfLowFreeze
@ lsfLowFreeze
Definition: LedgerFormats.h:292
ripple::NetClock::duration
std::chrono::duration< rep, period > duration
Definition: chrono.h:55
std::numeric_limits::max
T max(T... args)
ripple::STObject::getFieldU32
std::uint32_t getFieldU32(SField const &field) const
Definition: STObject.cpp:559
ripple::updateTrustLine
static bool updateTrustLine(ApplyView &view, SLE::pointer state, bool bSenderHigh, AccountID const &sender, STAmount const &before, STAmount const &after, beast::Journal j)
Definition: View.cpp:1250
ripple::rippleSend
static TER rippleSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1095
ripple::tecNO_AUTH
@ tecNO_AUTH
Definition: TER.h:281
ripple::sfLastLedgerSequence
const SF_UINT32 sfLastLedgerSequence
ripple::ApplyView::dirInsert
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition: ApplyView.h:306
ripple::sfAmendment
const SF_UINT256 sfAmendment
ripple::cdirFirst
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
Definition: View.cpp:134
ripple::STLedgerEntry::ref
const std::shared_ptr< STLedgerEntry > & ref
Definition: STLedgerEntry.h:37
ripple::sfMajorities
const SField sfMajorities
ripple::ReadView::open
virtual bool open() const =0
Returns true if this reflects an open ledger.
ripple::NetClock::time_point
std::chrono::time_point< NetClock > time_point
Definition: chrono.h:56
ripple::lsfHighReserve
@ lsfHighReserve
Definition: LedgerFormats.h:287
ripple::STObject::setFieldU32
void setFieldU32(SField const &field, std::uint32_t)
Definition: STObject.cpp:659
ripple::STAmount::getCurrency
Currency const & getCurrency() const
Definition: STAmount.h:356
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
std::set
STL class.
ripple::sfHighNode
const SF_UINT64 sfHighNode
ripple::isFrozen
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:203
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:603
ripple::noAccount
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:175
ripple::STLedgerEntry::pointer
std::shared_ptr< STLedgerEntry > pointer
Definition: STLedgerEntry.h:36
ripple::Issue::account
AccountID account
Definition: Issue.h:39
ripple::sfAmendments
const SF_VECTOR256 sfAmendments
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::STObject::getFieldH256
uint256 getFieldH256(SField const &field) const
Definition: STObject.cpp:583
ripple::root
Number root(Number f, unsigned d)
Definition: Number.cpp:624