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 xrpl {
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, "xrpl::directory::insertPage", "nextPage has default value");
139 /* Reserved for future use when directory pages may be inserted in
140 * between two other pages instead of only at the end of the chain.
141 if (nextPage)
142 node->setFieldU64(sfIndexNext, nextPage);
143 */
144 describe(node);
145 view.insert(node);
146
147 return page;
148}
149
150} // namespace directory
151
154 bool preserveOrder,
155 Keylet const& directory,
156 uint256 const& key,
157 std::function<void(std::shared_ptr<SLE> const&)> const& describe)
158{
159 auto root = peek(directory);
160
161 if (!root)
162 {
163 // No root, make it.
164 return directory::createRoot(*this, directory, key, describe);
165 }
166
167 auto [page, node, indexes] =
168 directory::findPreviousPage(*this, directory, root);
169
170 // If there's space, we use it:
171 if (indexes.size() < dirNodeMaxEntries)
172 {
174 *this, node, page, preserveOrder, indexes, key);
175 }
176
178 *this, page, node, 0, root, key, directory, describe);
179}
180
181bool
183{
184 auto node = peek(directory);
185
186 if (!node)
187 return false;
188
189 // Verify that the passed directory node is the directory root.
190 if (directory.type != ltDIR_NODE ||
191 node->getFieldH256(sfRootIndex) != directory.key)
192 {
193 // LCOV_EXCL_START
194 UNREACHABLE("xrpl::ApplyView::emptyDirDelete : invalid node type");
195 return false;
196 // LCOV_EXCL_STOP
197 }
198
199 // The directory still contains entries and so it cannot be removed
200 if (!node->getFieldV256(sfIndexes).empty())
201 return false;
202
203 std::uint64_t constexpr rootPage = 0;
204 auto prevPage = node->getFieldU64(sfIndexPrevious);
205 auto nextPage = node->getFieldU64(sfIndexNext);
206
207 if (nextPage == rootPage && prevPage != rootPage)
208 LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
209
210 if (prevPage == rootPage && nextPage != rootPage)
211 LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
212
213 // Older versions of the code would, in some cases, allow the last
214 // page to be empty. Remove such pages:
215 if (nextPage == prevPage && nextPage != rootPage)
216 {
217 auto last = peek(keylet::page(directory, nextPage));
218
219 if (!last)
220 { // LCOV_EXCL_START
221 LogicError("Directory chain: fwd link broken.");
222 // LCOV_EXCL_STOP
223 }
224
225 if (!last->getFieldV256(sfIndexes).empty())
226 return false;
227
228 // Update the first page's linked list and
229 // mark it as updated.
230 node->setFieldU64(sfIndexNext, rootPage);
231 node->setFieldU64(sfIndexPrevious, rootPage);
232 update(node);
233
234 // And erase the empty last page:
235 erase(last);
236
237 // Make sure our local values reflect the
238 // updated information:
239 nextPage = rootPage;
240 prevPage = rootPage;
241 }
242
243 // If there are no other pages, erase the root:
244 if (nextPage == rootPage && prevPage == rootPage)
245 erase(node);
246
247 return true;
248}
249
250bool
252 Keylet const& directory,
253 std::uint64_t page,
254 uint256 const& key,
255 bool keepRoot)
256{
257 auto node = peek(keylet::page(directory, page));
258
259 if (!node)
260 return false;
261
262 std::uint64_t constexpr rootPage = 0;
263
264 {
265 auto entries = node->getFieldV256(sfIndexes);
266
267 auto it = std::find(entries.begin(), entries.end(), key);
268
269 if (entries.end() == it)
270 return false;
271
272 // We always preserve the relative order when we remove.
273 entries.erase(it);
274
275 node->setFieldV256(sfIndexes, entries);
276 update(node);
277
278 if (!entries.empty())
279 return true;
280 }
281
282 // The current page is now empty; check if it can be
283 // deleted, and, if so, whether the entire directory
284 // can now be removed.
285 auto prevPage = node->getFieldU64(sfIndexPrevious);
286 auto nextPage = node->getFieldU64(sfIndexNext);
287
288 // The first page is the directory's root node and is
289 // treated specially: it can never be deleted even if
290 // it is empty, unless we plan on removing the entire
291 // directory.
292 if (page == rootPage)
293 {
294 if (nextPage == page && prevPage != page)
295 { // LCOV_EXCL_START
296 LogicError("Directory chain: fwd link broken");
297 // LCOV_EXCL_STOP
298 }
299
300 if (prevPage == page && nextPage != page)
301 { // LCOV_EXCL_START
302 LogicError("Directory chain: rev link broken");
303 // LCOV_EXCL_STOP
304 }
305
306 // Older versions of the code would, in some cases,
307 // allow the last page to be empty. Remove such
308 // pages if we stumble on them:
309 if (nextPage == prevPage && nextPage != page)
310 {
311 auto last = peek(keylet::page(directory, nextPage));
312 if (!last)
313 { // LCOV_EXCL_START
314 LogicError("Directory chain: fwd link broken.");
315 // LCOV_EXCL_STOP
316 }
317
318 if (last->getFieldV256(sfIndexes).empty())
319 {
320 // Update the first page's linked list and
321 // mark it as updated.
322 node->setFieldU64(sfIndexNext, page);
323 node->setFieldU64(sfIndexPrevious, page);
324 update(node);
325
326 // And erase the empty last page:
327 erase(last);
328
329 // Make sure our local values reflect the
330 // updated information:
331 nextPage = page;
332 prevPage = page;
333 }
334 }
335
336 if (keepRoot)
337 return true;
338
339 // If there's no other pages, erase the root:
340 if (nextPage == page && prevPage == page)
341 erase(node);
342
343 return true;
344 }
345
346 // This can never happen for nodes other than the root:
347 if (nextPage == page)
348 LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
349
350 if (prevPage == page)
351 LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
352
353 // This node isn't the root, so it can either be in the
354 // middle of the list, or at the end. Unlink it first
355 // and then check if that leaves the list with only a
356 // root:
357 auto prev = peek(keylet::page(directory, prevPage));
358 if (!prev)
359 LogicError("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
360 // Fix previous to point to its new next.
361 prev->setFieldU64(sfIndexNext, nextPage);
362 update(prev);
363
364 auto next = peek(keylet::page(directory, nextPage));
365 if (!next)
366 LogicError("Directory chain: rev link broken."); // LCOV_EXCL_LINE
367 // Fix next to point to its new previous.
368 next->setFieldU64(sfIndexPrevious, prevPage);
369 update(next);
370
371 // The page is no longer linked. Delete it.
372 erase(node);
373
374 // Check whether the next page is the last page and, if
375 // so, whether it's empty. If it is, delete it.
376 if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
377 next->getFieldV256(sfIndexes).empty())
378 {
379 // Since next doesn't point to the root, it
380 // can't be pointing to prev.
381 erase(next);
382
383 // The previous page is now the last page:
384 prev->setFieldU64(sfIndexNext, rootPage);
385 update(prev);
386
387 // And the root points to the last page:
388 auto root = peek(keylet::page(directory, rootPage));
389 if (!root)
390 { // LCOV_EXCL_START
391 LogicError("Directory chain: root link broken.");
392 // LCOV_EXCL_STOP
393 }
394 root->setFieldU64(sfIndexPrevious, prevPage);
395 update(root);
396
397 nextPage = rootPage;
398 }
399
400 // If we're not keeping the root, then check to see if
401 // it's left empty. If so, delete it as well.
402 if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
403 {
404 if (prev->getFieldV256(sfIndexes).empty())
405 erase(prev);
406 }
407
408 return true;
409}
410
411bool
413 Keylet const& directory,
414 std::function<void(uint256 const&)> const& callback)
415{
417
418 do
419 {
420 auto const page = peek(keylet::page(directory, pi.value_or(0)));
421
422 if (!page)
423 return false;
424
425 for (auto const& item : page->getFieldV256(sfIndexes))
426 callback(item);
427
428 pi = (*page)[~sfIndexNext];
429
430 erase(page);
431 } while (pi);
432
433 return true;
434}
435
436} // namespace xrpl
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:124
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.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
bool dirDelete(Keylet const &directory, std::function< void(uint256 const &)> const &)
Remove the specified directory, invoking the callback for every node.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
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 std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
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 insert(std::vector< uint256 >::const_iterator pos, uint256 const &value)
std::vector< uint256 >::iterator begin()
void push_back(uint256 const &v)
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)
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
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
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:362
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
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:644
std::uint64_t constexpr dirNodeMaxPages
The maximum number of pages allowed in a directory.
Definition Protocol.h:44
T sort(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
LedgerEntryType type
Definition Keylet.h:22
T value_or(T... args)