rippled
ApplyView.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/contract.h>
21 #include <ripple/ledger/ApplyView.h>
22 #include <ripple/protocol/Protocol.h>
23 #include <cassert>
24 
25 namespace ripple {
26 
29  bool preserveOrder,
30  Keylet const& directory,
31  uint256 const& key,
32  std::function<void(std::shared_ptr<SLE> const&)> const& describe)
33 {
34  auto root = peek(directory);
35 
36  if (!root)
37  {
38  // No root, make it.
39  root = std::make_shared<SLE>(directory);
40  root->setFieldH256(sfRootIndex, directory.key);
41  describe(root);
42 
43  STVector256 v;
44  v.push_back(key);
45  root->setFieldV256(sfIndexes, v);
46 
47  insert(root);
48  return std::uint64_t{0};
49  }
50 
51  std::uint64_t page = root->getFieldU64(sfIndexPrevious);
52 
53  auto node = root;
54 
55  if (page)
56  {
57  node = peek(keylet::page(directory, page));
58  if (!node)
59  LogicError("Directory chain: root back-pointer broken.");
60  }
61 
62  auto indexes = node->getFieldV256(sfIndexes);
63 
64  // If there's space, we use it:
65  if (indexes.size() < dirNodeMaxEntries)
66  {
67  if (preserveOrder)
68  {
69  if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
70  LogicError("dirInsert: double insertion");
71 
72  indexes.push_back(key);
73  }
74  else
75  {
76  // We can't be sure if this page is already sorted because
77  // it may be a legacy page we haven't yet touched. Take
78  // the time to sort it.
79  std::sort(indexes.begin(), indexes.end());
80 
81  auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
82 
83  if (pos != indexes.end() && key == *pos)
84  LogicError("dirInsert: double insertion");
85 
86  indexes.insert(pos, key);
87  }
88 
89  node->setFieldV256(sfIndexes, indexes);
90  update(node);
91  return page;
92  }
93 
94  // Check whether we're out of pages.
95  if (++page >= dirNodeMaxPages)
96  return std::nullopt;
97 
98  // We are about to create a new node; we'll link it to
99  // the chain first:
100  node->setFieldU64(sfIndexNext, page);
101  update(node);
102 
103  root->setFieldU64(sfIndexPrevious, page);
104  update(root);
105 
106  // Insert the new key:
107  indexes.clear();
108  indexes.push_back(key);
109 
110  node = std::make_shared<SLE>(keylet::page(directory, page));
111  node->setFieldH256(sfRootIndex, directory.key);
112  node->setFieldV256(sfIndexes, indexes);
113 
114  // Save some space by not specifying the value 0 since
115  // it's the default.
116  if (page != 1)
117  node->setFieldU64(sfIndexPrevious, page - 1);
118  describe(node);
119  insert(node);
120 
121  return page;
122 }
123 
124 bool
126 {
127  auto node = peek(directory);
128 
129  if (!node)
130  return false;
131 
132  // Verify that the passed directory node is the directory root.
133  if (directory.type != ltDIR_NODE ||
134  node->getFieldH256(sfRootIndex) != directory.key)
135  {
136  assert(!"emptyDirDelete() called with wrong node type");
137  return false;
138  }
139 
140  // The directory still contains entries and so it cannot be removed
141  if (!node->getFieldV256(sfIndexes).empty())
142  return false;
143 
144  std::uint64_t constexpr rootPage = 0;
145  auto prevPage = node->getFieldU64(sfIndexPrevious);
146  auto nextPage = node->getFieldU64(sfIndexNext);
147 
148  if (nextPage == rootPage && prevPage != rootPage)
149  LogicError("Directory chain: fwd link broken");
150 
151  if (prevPage == rootPage && nextPage != rootPage)
152  LogicError("Directory chain: rev link broken");
153 
154  // Older versions of the code would, in some cases, allow the last
155  // page to be empty. Remove such pages:
156  if (nextPage == prevPage && nextPage != rootPage)
157  {
158  auto last = peek(keylet::page(directory, nextPage));
159 
160  if (!last)
161  LogicError("Directory chain: fwd link broken.");
162 
163  if (!last->getFieldV256(sfIndexes).empty())
164  return false;
165 
166  // Update the first page's linked list and
167  // mark it as updated.
168  node->setFieldU64(sfIndexNext, rootPage);
169  node->setFieldU64(sfIndexPrevious, rootPage);
170  update(node);
171 
172  // And erase the empty last page:
173  erase(last);
174 
175  // Make sure our local values reflect the
176  // updated information:
177  nextPage = rootPage;
178  prevPage = rootPage;
179  }
180 
181  // If there are no other pages, erase the root:
182  if (nextPage == rootPage && prevPage == rootPage)
183  erase(node);
184 
185  return true;
186 }
187 
188 bool
190  Keylet const& directory,
191  std::uint64_t page,
192  uint256 const& key,
193  bool keepRoot)
194 {
195  auto node = peek(keylet::page(directory, page));
196 
197  if (!node)
198  return false;
199 
200  std::uint64_t constexpr rootPage = 0;
201 
202  {
203  auto entries = node->getFieldV256(sfIndexes);
204 
205  auto it = std::find(entries.begin(), entries.end(), key);
206 
207  if (entries.end() == it)
208  return false;
209 
210  // We always preserve the relative order when we remove.
211  entries.erase(it);
212 
213  node->setFieldV256(sfIndexes, entries);
214  update(node);
215 
216  if (!entries.empty())
217  return true;
218  }
219 
220  // The current page is now empty; check if it can be
221  // deleted, and, if so, whether the entire directory
222  // can now be removed.
223  auto prevPage = node->getFieldU64(sfIndexPrevious);
224  auto nextPage = node->getFieldU64(sfIndexNext);
225 
226  // The first page is the directory's root node and is
227  // treated specially: it can never be deleted even if
228  // it is empty, unless we plan on removing the entire
229  // directory.
230  if (page == rootPage)
231  {
232  if (nextPage == page && prevPage != page)
233  LogicError("Directory chain: fwd link broken");
234 
235  if (prevPage == page && nextPage != page)
236  LogicError("Directory chain: rev link broken");
237 
238  // Older versions of the code would, in some cases,
239  // allow the last page to be empty. Remove such
240  // pages if we stumble on them:
241  if (nextPage == prevPage && nextPage != page)
242  {
243  auto last = peek(keylet::page(directory, nextPage));
244  if (!last)
245  LogicError("Directory chain: fwd link broken.");
246 
247  if (last->getFieldV256(sfIndexes).empty())
248  {
249  // Update the first page's linked list and
250  // mark it as updated.
251  node->setFieldU64(sfIndexNext, page);
252  node->setFieldU64(sfIndexPrevious, page);
253  update(node);
254 
255  // And erase the empty last page:
256  erase(last);
257 
258  // Make sure our local values reflect the
259  // updated information:
260  nextPage = page;
261  prevPage = page;
262  }
263  }
264 
265  if (keepRoot)
266  return true;
267 
268  // If there's no other pages, erase the root:
269  if (nextPage == page && prevPage == page)
270  erase(node);
271 
272  return true;
273  }
274 
275  // This can never happen for nodes other than the root:
276  if (nextPage == page)
277  LogicError("Directory chain: fwd link broken");
278 
279  if (prevPage == page)
280  LogicError("Directory chain: rev link broken");
281 
282  // This node isn't the root, so it can either be in the
283  // middle of the list, or at the end. Unlink it first
284  // and then check if that leaves the list with only a
285  // root:
286  auto prev = peek(keylet::page(directory, prevPage));
287  if (!prev)
288  LogicError("Directory chain: fwd link broken.");
289  // Fix previous to point to its new next.
290  prev->setFieldU64(sfIndexNext, nextPage);
291  update(prev);
292 
293  auto next = peek(keylet::page(directory, nextPage));
294  if (!next)
295  LogicError("Directory chain: rev link broken.");
296  // Fix next to point to its new previous.
297  next->setFieldU64(sfIndexPrevious, prevPage);
298  update(next);
299 
300  // The page is no longer linked. Delete it.
301  erase(node);
302 
303  // Check whether the next page is the last page and, if
304  // so, whether it's empty. If it is, delete it.
305  if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
306  next->getFieldV256(sfIndexes).empty())
307  {
308  // Since next doesn't point to the root, it
309  // can't be pointing to prev.
310  erase(next);
311 
312  // The previous page is now the last page:
313  prev->setFieldU64(sfIndexNext, rootPage);
314  update(prev);
315 
316  // And the root points to the the last page:
317  auto root = peek(keylet::page(directory, rootPage));
318  if (!root)
319  LogicError("Directory chain: root link broken.");
320  root->setFieldU64(sfIndexPrevious, prevPage);
321  update(root);
322 
323  nextPage = rootPage;
324  }
325 
326  // If we're not keeping the root, then check to see if
327  // it's left empty. If so, delete it as well.
328  if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
329  {
330  if (prev->getFieldV256(sfIndexes).empty())
331  erase(prev);
332  }
333 
334  return true;
335 }
336 
337 bool
339  Keylet const& directory,
340  std::function<void(uint256 const&)> const& callback)
341 {
343 
344  do
345  {
346  auto const page = peek(keylet::page(directory, pi.value_or(0)));
347 
348  if (!page)
349  return false;
350 
351  for (auto const& item : page->getFieldV256(sfIndexes))
352  callback(item);
353 
354  pi = (*page)[~sfIndexNext];
355 
356  erase(page);
357  } while (pi);
358 
359  return true;
360 }
361 
362 } // namespace ripple
ripple::sfIndexNext
const SF_UINT64 sfIndexNext
ripple::sfRootIndex
const SF_UINT256 sfRootIndex
ripple::Keylet
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:38
std::shared_ptr
STL class.
ripple::ApplyView::peek
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
ripple::ApplyView::erase
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
std::find
T find(T... args)
ripple::STVector256::push_back
void push_back(uint256 const &v)
Definition: STVector256.h:216
std::optional::value_or
T value_or(T... args)
ripple::dirNodeMaxPages
constexpr std::uint64_t dirNodeMaxPages
The maximum number of pages allowed in a directory.
Definition: Protocol.h:58
ripple::ApplyView::update
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
std::function
std::sort
T sort(T... args)
ripple::ltDIR_NODE
@ ltDIR_NODE
A ledger object which contains a list of object identifiers.
Definition: LedgerFormats.h:66
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< 256 >
ripple::sfIndexPrevious
const SF_UINT64 sfIndexPrevious
ripple::keylet::page
Keylet page(uint256 const &key, std::uint64_t index) noexcept
A page in a directory.
Definition: Indexes.cpp:309
ripple::ApplyView::dirAdd
std::optional< std::uint64_t > dirAdd(bool preserveOrder, Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Add an entry to a directory using the specified insert strategy.
Definition: ApplyView.cpp:28
ripple::Keylet::type
LedgerEntryType type
Definition: Keylet.h:41
ripple::ApplyView::dirDelete
bool dirDelete(Keylet const &directory, std::function< void(uint256 const &)> const &)
Remove the specified directory, invoking the callback for every node.
Definition: ApplyView.cpp:338
std::uint64_t
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
std::lower_bound
T lower_bound(T... args)
ripple::ApplyView::emptyDirDelete
bool emptyDirDelete(Keylet const &directory)
Remove the specified directory, if it is empty.
Definition: ApplyView.cpp:125
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
cassert
ripple::STVector256
Definition: STVector256.h:29
std::optional
ripple::dirNodeMaxEntries
constexpr std::size_t dirNodeMaxEntries
The maximum number of entries per directory page.
Definition: Protocol.h:55
ripple::root
Number root(Number f, unsigned d)
Definition: Number.cpp:624