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