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