rippled
Loading...
Searching...
No Matches
ApplyView.cpp
1#include <xrpl/basics/contract.h>
2#include <xrpl/beast/utility/instrumentation.h>
3#include <xrpl/ledger/ApplyView.h>
4#include <xrpl/protocol/Protocol.h>
5
6#include <limits>
7#include <type_traits>
8
9namespace ripple {
10
11namespace directory {
12
15 ApplyView& view,
16 Keylet const& directory,
17 uint256 const& key,
18 std::function<void(std::shared_ptr<SLE> const&)> const& describe)
19{
20 auto newRoot = std::make_shared<SLE>(directory);
21 newRoot->setFieldH256(sfRootIndex, directory.key);
22 describe(newRoot);
23
25 v.push_back(key);
26 newRoot->setFieldV256(sfIndexes, v);
27
28 view.insert(newRoot);
29 return std::uint64_t{0};
30}
31
32auto
33findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start)
34{
35 std::uint64_t page = start->getFieldU64(sfIndexPrevious);
36
37 auto node = start;
38
39 if (page)
40 {
41 node = view.peek(keylet::page(directory, page));
42 if (!node)
43 { // LCOV_EXCL_START
44 LogicError("Directory chain: root back-pointer broken.");
45 // LCOV_EXCL_STOP
46 }
47 }
48
49 auto indexes = node->getFieldV256(sfIndexes);
50 return std::make_tuple(page, node, indexes);
51}
52
55 ApplyView& view,
56 SLE::ref node,
57 std::uint64_t page,
58 bool preserveOrder,
59 STVector256& indexes,
60 uint256 const& key)
61{
62 if (preserveOrder)
63 {
64 if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
65 LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
66
67 indexes.push_back(key);
68 }
69 else
70 {
71 // We can't be sure if this page is already sorted because
72 // it may be a legacy page we haven't yet touched. Take
73 // the time to sort it.
74 std::sort(indexes.begin(), indexes.end());
75
76 auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
77
78 if (pos != indexes.end() && key == *pos)
79 LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
80
81 indexes.insert(pos, key);
82 }
83
84 node->setFieldV256(sfIndexes, indexes);
85 view.update(node);
86 return page;
87}
88
91 ApplyView& view,
92 std::uint64_t page,
93 SLE::pointer node,
94 std::uint64_t nextPage,
95 SLE::ref next,
96 uint256 const& key,
97 Keylet const& directory,
98 std::function<void(std::shared_ptr<SLE> const&)> const& describe)
99{
100 // We rely on modulo arithmetic of unsigned integers (guaranteed in
101 // [basic.fundamental] paragraph 2) to detect page representation overflow.
102 // For signed integers this would be UB, hence static_assert here.
103 static_assert(std::is_unsigned_v<decltype(page)>);
104 // Defensive check against breaking changes in compiler.
105 static_assert([]<typename T>(std::type_identity<T>) constexpr -> T {
107 return ++tmp;
108 }(std::type_identity<decltype(page)>{}) == 0);
109 ++page;
110 // Check whether we're out of pages.
111 if (page == 0)
112 return std::nullopt;
113 if (!view.rules().enabled(fixDirectoryLimit) &&
114 page >= dirNodeMaxPages) // Old pages limit
115 return std::nullopt;
116
117 // We are about to create a new node; we'll link it to
118 // the chain first:
119 node->setFieldU64(sfIndexNext, page);
120 view.update(node);
121
122 next->setFieldU64(sfIndexPrevious, page);
123 view.update(next);
124
125 // Insert the new key:
126 STVector256 indexes;
127 indexes.push_back(key);
128
129 node = std::make_shared<SLE>(keylet::page(directory, page));
130 node->setFieldH256(sfRootIndex, directory.key);
131 node->setFieldV256(sfIndexes, indexes);
132
133 // Save some space by not specifying the value 0 since
134 // it's the default.
135 if (page != 1)
136 node->setFieldU64(sfIndexPrevious, page - 1);
137 XRPL_ASSERT_PARTS(
138 !nextPage,
139 "ripple::directory::insertPage",
140 "nextPage has default value");
141 /* Reserved for future use when directory pages may be inserted in
142 * between two other pages instead of only at the end of the chain.
143 if (nextPage)
144 node->setFieldU64(sfIndexNext, nextPage);
145 */
146 describe(node);
147 view.insert(node);
148
149 return page;
150}
151
152} // namespace directory
153
156 bool preserveOrder,
157 Keylet const& directory,
158 uint256 const& key,
159 std::function<void(std::shared_ptr<SLE> const&)> const& describe)
160{
161 auto root = peek(directory);
162
163 if (!root)
164 {
165 // No root, make it.
166 return directory::createRoot(*this, directory, key, describe);
167 }
168
169 auto [page, node, indexes] =
170 directory::findPreviousPage(*this, directory, root);
171
172 // If there's space, we use it:
173 if (indexes.size() < dirNodeMaxEntries)
174 {
176 *this, node, page, preserveOrder, indexes, key);
177 }
178
180 *this, page, node, 0, root, key, directory, describe);
181}
182
183bool
185{
186 auto node = peek(directory);
187
188 if (!node)
189 return false;
190
191 // Verify that the passed directory node is the directory root.
192 if (directory.type != ltDIR_NODE ||
193 node->getFieldH256(sfRootIndex) != directory.key)
194 {
195 // LCOV_EXCL_START
196 UNREACHABLE("ripple::ApplyView::emptyDirDelete : invalid node type");
197 return false;
198 // LCOV_EXCL_STOP
199 }
200
201 // The directory still contains entries and so it cannot be removed
202 if (!node->getFieldV256(sfIndexes).empty())
203 return false;
204
205 std::uint64_t constexpr rootPage = 0;
206 auto prevPage = node->getFieldU64(sfIndexPrevious);
207 auto nextPage = node->getFieldU64(sfIndexNext);
208
209 if (nextPage == rootPage && prevPage != rootPage)
210 LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
211
212 if (prevPage == rootPage && nextPage != rootPage)
213 LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
214
215 // Older versions of the code would, in some cases, allow the last
216 // page to be empty. Remove such pages:
217 if (nextPage == prevPage && nextPage != rootPage)
218 {
219 auto last = peek(keylet::page(directory, nextPage));
220
221 if (!last)
222 { // LCOV_EXCL_START
223 LogicError("Directory chain: fwd link broken.");
224 // LCOV_EXCL_STOP
225 }
226
227 if (!last->getFieldV256(sfIndexes).empty())
228 return false;
229
230 // Update the first page's linked list and
231 // mark it as updated.
232 node->setFieldU64(sfIndexNext, rootPage);
233 node->setFieldU64(sfIndexPrevious, rootPage);
234 update(node);
235
236 // And erase the empty last page:
237 erase(last);
238
239 // Make sure our local values reflect the
240 // updated information:
241 nextPage = rootPage;
242 prevPage = rootPage;
243 }
244
245 // If there are no other pages, erase the root:
246 if (nextPage == rootPage && prevPage == rootPage)
247 erase(node);
248
249 return true;
250}
251
252bool
254 Keylet const& directory,
255 std::uint64_t page,
256 uint256 const& key,
257 bool keepRoot)
258{
259 auto node = peek(keylet::page(directory, page));
260
261 if (!node)
262 return false;
263
264 std::uint64_t constexpr rootPage = 0;
265
266 {
267 auto entries = node->getFieldV256(sfIndexes);
268
269 auto it = std::find(entries.begin(), entries.end(), key);
270
271 if (entries.end() == it)
272 return false;
273
274 // We always preserve the relative order when we remove.
275 entries.erase(it);
276
277 node->setFieldV256(sfIndexes, entries);
278 update(node);
279
280 if (!entries.empty())
281 return true;
282 }
283
284 // The current page is now empty; check if it can be
285 // deleted, and, if so, whether the entire directory
286 // can now be removed.
287 auto prevPage = node->getFieldU64(sfIndexPrevious);
288 auto nextPage = node->getFieldU64(sfIndexNext);
289
290 // The first page is the directory's root node and is
291 // treated specially: it can never be deleted even if
292 // it is empty, unless we plan on removing the entire
293 // directory.
294 if (page == rootPage)
295 {
296 if (nextPage == page && prevPage != page)
297 { // LCOV_EXCL_START
298 LogicError("Directory chain: fwd link broken");
299 // LCOV_EXCL_STOP
300 }
301
302 if (prevPage == page && nextPage != page)
303 { // LCOV_EXCL_START
304 LogicError("Directory chain: rev link broken");
305 // LCOV_EXCL_STOP
306 }
307
308 // Older versions of the code would, in some cases,
309 // allow the last page to be empty. Remove such
310 // pages if we stumble on them:
311 if (nextPage == prevPage && nextPage != page)
312 {
313 auto last = peek(keylet::page(directory, nextPage));
314 if (!last)
315 { // LCOV_EXCL_START
316 LogicError("Directory chain: fwd link broken.");
317 // LCOV_EXCL_STOP
318 }
319
320 if (last->getFieldV256(sfIndexes).empty())
321 {
322 // Update the first page's linked list and
323 // mark it as updated.
324 node->setFieldU64(sfIndexNext, page);
325 node->setFieldU64(sfIndexPrevious, page);
326 update(node);
327
328 // And erase the empty last page:
329 erase(last);
330
331 // Make sure our local values reflect the
332 // updated information:
333 nextPage = page;
334 prevPage = page;
335 }
336 }
337
338 if (keepRoot)
339 return true;
340
341 // If there's no other pages, erase the root:
342 if (nextPage == page && prevPage == page)
343 erase(node);
344
345 return true;
346 }
347
348 // This can never happen for nodes other than the root:
349 if (nextPage == page)
350 LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
351
352 if (prevPage == page)
353 LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
354
355 // This node isn't the root, so it can either be in the
356 // middle of the list, or at the end. Unlink it first
357 // and then check if that leaves the list with only a
358 // root:
359 auto prev = peek(keylet::page(directory, prevPage));
360 if (!prev)
361 LogicError("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
362 // Fix previous to point to its new next.
363 prev->setFieldU64(sfIndexNext, nextPage);
364 update(prev);
365
366 auto next = peek(keylet::page(directory, nextPage));
367 if (!next)
368 LogicError("Directory chain: rev link broken."); // LCOV_EXCL_LINE
369 // Fix next to point to its new previous.
370 next->setFieldU64(sfIndexPrevious, prevPage);
371 update(next);
372
373 // The page is no longer linked. Delete it.
374 erase(node);
375
376 // Check whether the next page is the last page and, if
377 // so, whether it's empty. If it is, delete it.
378 if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
379 next->getFieldV256(sfIndexes).empty())
380 {
381 // Since next doesn't point to the root, it
382 // can't be pointing to prev.
383 erase(next);
384
385 // The previous page is now the last page:
386 prev->setFieldU64(sfIndexNext, rootPage);
387 update(prev);
388
389 // And the root points to the last page:
390 auto root = peek(keylet::page(directory, rootPage));
391 if (!root)
392 { // LCOV_EXCL_START
393 LogicError("Directory chain: root link broken.");
394 // LCOV_EXCL_STOP
395 }
396 root->setFieldU64(sfIndexPrevious, prevPage);
397 update(root);
398
399 nextPage = rootPage;
400 }
401
402 // If we're not keeping the root, then check to see if
403 // it's left empty. If so, delete it as well.
404 if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
405 {
406 if (prev->getFieldV256(sfIndexes).empty())
407 erase(prev);
408 }
409
410 return true;
411}
412
413bool
415 Keylet const& directory,
416 std::function<void(uint256 const&)> const& callback)
417{
419
420 do
421 {
422 auto const page = peek(keylet::page(directory, pi.value_or(0)));
423
424 if (!page)
425 return false;
426
427 for (auto const& item : page->getFieldV256(sfIndexes))
428 callback(item);
429
430 pi = (*page)[~sfIndexNext];
431
432 erase(page);
433 } while (pi);
434
435 return true;
436}
437
438} // namespace ripple
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:124
bool dirDelete(Keylet const &directory, std::function< void(uint256 const &)> const &)
Remove the specified directory, invoking the callback for every node.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
bool emptyDirDelete(Keylet const &directory)
Remove the specified directory, if it is empty.
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.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:111
std::vector< uint256 >::iterator begin()
void push_back(uint256 const &v)
std::vector< uint256 >::iterator insert(std::vector< uint256 >::const_iterator pos, uint256 const &value)
std::vector< uint256 >::iterator end()
T find(T... args)
T is_same_v
T is_unsigned_v
T lower_bound(T... args)
T make_tuple(T... args)
T max(T... args)
std::uint64_t createRoot(ApplyView &view, Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Helper functions for managing low-level directory operations.
Definition ApplyView.cpp:14
auto findPreviousPage(ApplyView &view, Keylet const &directory, SLE::ref start)
Definition ApplyView.cpp:33
std::uint64_t insertKey(ApplyView &view, SLE::ref node, std::uint64_t page, bool preserveOrder, STVector256 &indexes, uint256 const &key)
Definition ApplyView.cpp:54
std::optional< std::uint64_t > insertPage(ApplyView &view, std::uint64_t page, SLE::pointer node, std::uint64_t nextPage, SLE::ref next, uint256 const &key, Keylet const &directory, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Definition ApplyView.cpp:90
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:363
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::uint64_t constexpr dirNodeMaxPages
The maximum number of pages allowed in a directory.
Definition Protocol.h:44
std::size_t constexpr dirNodeMaxEntries
The maximum number of entries per directory page.
Definition Protocol.h:38
Number root(Number f, unsigned d)
Definition Number.cpp:645
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
T sort(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
LedgerEntryType type
Definition Keylet.h:22
uint256 key
Definition Keylet.h:21
T value_or(T... args)