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