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