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