xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
libxrpl
shamap
SHAMapDelta.cpp
1
#include <xrpl/basics/IntrusivePointer.h>
// IWYU pragma: keep
2
#include <xrpl/basics/IntrusivePointer.ipp>
// IWYU pragma: keep
3
#include <xrpl/basics/Log.h>
4
#include <xrpl/basics/contract.h>
5
#include <xrpl/basics/safe_cast.h>
6
#include <xrpl/beast/utility/instrumentation.h>
7
#include <xrpl/shamap/SHAMap.h>
8
#include <xrpl/shamap/SHAMapInnerNode.h>
9
#include <xrpl/shamap/SHAMapItem.h>
10
#include <xrpl/shamap/SHAMapMissingNode.h>
11
#include <xrpl/shamap/SHAMapTreeNode.h>
12
13
#include <boost/smart_ptr/intrusive_ptr.hpp>
14
15
#include <
array
>
16
#include <
mutex
>
17
#include <
sstream
>
18
#include <
stack
>
19
#include <
thread
>
20
#include <
utility
>
21
#include <
vector
>
22
23
namespace
xrpl
{
24
25
// This code is used to compare another node's transaction tree
26
// to our own. It returns a map containing all items that are different
27
// between two SHA maps. It is optimized not to descend down tree
28
// branches with the same branch hash. A limit can be passed so
29
// that we will abort early if a node sends a map to us that
30
// makes no sense at all. (And our sync algorithm will avoid
31
// synchronizing matching branches too.)
32
33
bool
34
SHAMap::walkBranch
(
35
SHAMapTreeNode
* node,
36
boost::intrusive_ptr<SHAMapItem const>
const
& otherMapItem,
37
bool
isFirstMap,
38
Delta
& differences,
39
int
& maxCount)
const
40
{
41
// Walk a branch of a SHAMap that's matched by an empty branch or single
42
// item in the other map
43
std::stack<SHAMapTreeNode*, std::vector<SHAMapTreeNode*>
> nodeStack;
44
nodeStack.
push
(node);
45
46
bool
emptyBranch = !otherMapItem;
47
48
while
(!nodeStack.
empty
())
49
{
50
node = nodeStack.
top
();
51
nodeStack.
pop
();
52
53
if
(node->
isInner
())
54
{
55
// This is an inner node, add all non-empty branches
56
auto
inner =
safeDowncast<SHAMapInnerNode*>
(node);
57
for
(
auto
i = 0u; i <
SHAMapInnerNode::kBranchFactor
; ++i)
58
{
59
if
(!inner->isEmptyBranch(i))
60
nodeStack.
push
({
descendThrow
(inner, i)});
61
}
62
}
63
else
64
{
65
// This is a leaf node, process its item
66
auto
item =
safeDowncast<SHAMapLeafNode*>
(node)->peekItem();
67
68
if
(emptyBranch || (item->key() != otherMapItem->key()))
69
{
70
// unmatched
71
if
(isFirstMap)
72
{
73
differences.
insert
(
std::make_pair
(item->key(),
DeltaRef
(item,
nullptr
)));
74
}
75
else
76
{
77
differences.
insert
(
std::make_pair
(item->key(),
DeltaRef
(
nullptr
, item)));
78
}
79
80
if
(--maxCount <= 0)
81
return
false
;
82
}
83
else
if
(item->slice() != otherMapItem->slice())
84
{
85
// non-matching items with same tag
86
if
(isFirstMap)
87
{
88
differences.
insert
(
std::make_pair
(item->key(),
DeltaRef
(item, otherMapItem)));
89
}
90
else
91
{
92
differences.
insert
(
std::make_pair
(item->key(),
DeltaRef
(otherMapItem, item)));
93
}
94
95
if
(--maxCount <= 0)
96
return
false
;
97
98
emptyBranch =
true
;
99
}
100
else
101
{
102
// exact match
103
emptyBranch =
true
;
104
}
105
}
106
}
107
108
if
(!emptyBranch)
109
{
110
// otherMapItem was unmatched, must add
111
if
(isFirstMap)
112
{
// this is first map, so other item is from second
113
differences.
insert
(
114
std::make_pair
(otherMapItem->key(),
DeltaRef
(
nullptr
, otherMapItem)));
115
}
116
else
117
{
118
differences.
insert
(
119
std::make_pair
(otherMapItem->key(),
DeltaRef
(otherMapItem,
nullptr
)));
120
}
121
122
if
(--maxCount <= 0)
123
return
false
;
124
}
125
126
return
true
;
127
}
128
129
bool
130
SHAMap::compare
(
SHAMap
const
& otherMap,
Delta
& differences,
int
maxCount)
const
131
{
132
// compare two hash trees, add up to maxCount differences to the difference
133
// table return value: true=complete table of differences given, false=too
134
// many differences throws on corrupt tables or missing nodes CAUTION:
135
// otherMap is not locked and must be immutable
136
137
XRPL_ASSERT(
138
isValid
() && otherMap.
isValid
(),
"xrpl::SHAMap::compare : valid state and valid input"
);
139
140
if
(
getHash
() == otherMap.
getHash
())
141
return
true
;
142
143
using
StackEntry =
std::pair<SHAMapTreeNode*, SHAMapTreeNode*>
;
144
std::stack<StackEntry, std::vector<StackEntry>
> nodeStack;
// track nodes we've pushed
145
146
nodeStack.
emplace
(
root_
.get(), otherMap.
root_
.
get
());
147
while
(!nodeStack.
empty
())
148
{
149
auto
[ourNode, otherNode] = nodeStack.
top
();
150
nodeStack.
pop
();
151
152
if
((ourNode ==
nullptr
) || (otherNode ==
nullptr
))
153
{
154
// LCOV_EXCL_START
155
UNREACHABLE(
"xrpl::SHAMap::compare : missing a node"
);
156
Throw<SHAMapMissingNode>
(
type_
,
UInt256
());
157
// LCOV_EXCL_STOP
158
}
159
160
if
(ourNode->isLeaf() && otherNode->isLeaf())
161
{
162
// two leaves
163
auto
ours =
safeDowncast<SHAMapLeafNode*>
(ourNode);
164
auto
other =
safeDowncast<SHAMapLeafNode*>
(otherNode);
165
if
(ours->peekItem()->key() == other->peekItem()->key())
166
{
167
if
(ours->peekItem()->slice() != other->peekItem()->slice())
168
{
169
differences.
insert
(
170
std::make_pair
(
171
ours->peekItem()->key(),
172
DeltaRef
(ours->peekItem(), other->peekItem())));
173
if
(--maxCount <= 0)
174
return
false
;
175
}
176
}
177
else
178
{
179
differences.
insert
(
180
std::make_pair
(ours->peekItem()->key(),
DeltaRef
(ours->peekItem(),
nullptr
)));
181
if
(--maxCount <= 0)
182
return
false
;
183
184
differences.
insert
(
185
std::make_pair
(other->peekItem()->key(),
DeltaRef
(
nullptr
, other->peekItem())));
186
if
(--maxCount <= 0)
187
return
false
;
188
}
189
}
190
else
if
(ourNode->isInner() && otherNode->isLeaf())
191
{
192
auto
ours =
safeDowncast<SHAMapInnerNode*>
(ourNode);
193
auto
other =
safeDowncast<SHAMapLeafNode*>
(otherNode);
194
if
(!
walkBranch
(ours, other->peekItem(),
true
, differences, maxCount))
195
return
false
;
196
}
197
else
if
(ourNode->isLeaf() && otherNode->isInner())
198
{
199
auto
ours =
safeDowncast<SHAMapLeafNode*>
(ourNode);
200
auto
other =
safeDowncast<SHAMapInnerNode*>
(otherNode);
201
if
(!otherMap.
walkBranch
(other, ours->peekItem(),
false
, differences, maxCount))
202
return
false
;
203
}
204
else
if
(ourNode->isInner() && otherNode->isInner())
205
{
206
auto
ours =
safeDowncast<SHAMapInnerNode*>
(ourNode);
207
auto
other =
safeDowncast<SHAMapInnerNode*>
(otherNode);
208
for
(
auto
i = 0u; i <
SHAMapInnerNode::kBranchFactor
; ++i)
209
{
210
if
(ours->getChildHash(i) != other->getChildHash(i))
211
{
212
if
(other->isEmptyBranch(i))
213
{
214
// We have a branch, the other tree does not
215
SHAMapTreeNode
* iNode =
descendThrow
(ours, i);
216
if
(!
walkBranch
(iNode,
nullptr
,
true
, differences, maxCount))
217
return
false
;
218
}
219
else
if
(ours->isEmptyBranch(i))
220
{
221
// The other tree has a branch, we do not
222
SHAMapTreeNode
* iNode = otherMap.
descendThrow
(other, i);
223
if
(!otherMap.
walkBranch
(iNode,
nullptr
,
false
, differences, maxCount))
224
return
false
;
225
}
226
else
227
{
// The two trees have different non-empty branches
228
nodeStack.
emplace
(
descendThrow
(ours, i), otherMap.
descendThrow
(other, i));
229
}
230
}
231
}
232
}
233
else
234
{
235
// LCOV_EXCL_START
236
UNREACHABLE(
"xrpl::SHAMap::compare : invalid node"
);
237
// LCOV_EXCL_STOP
238
}
239
}
240
241
return
true
;
242
}
243
244
void
245
SHAMap::walkMap
(
std::vector<SHAMapMissingNode>
& missingNodes,
int
maxMissing)
const
246
{
247
if
(!
root_
->isInner())
// root_ is only node, and we have it
248
return
;
249
250
using
StackEntry =
intr_ptr::SharedPtr<SHAMapInnerNode>
;
251
std::stack<StackEntry, std::vector<StackEntry>
> nodeStack;
252
253
nodeStack.
push
(
intr_ptr::staticPointerCast<SHAMapInnerNode>
(
root_
));
254
255
while
(!nodeStack.
empty
())
256
{
257
intr_ptr::SharedPtr<SHAMapInnerNode>
const
node = std::move(nodeStack.
top
());
258
nodeStack.
pop
();
259
260
for
(
auto
i = 0u; i <
SHAMapInnerNode::kBranchFactor
; ++i)
261
{
262
if
(!node->isEmptyBranch(i))
263
{
264
SHAMapTreeNodePtr
const
nextNode =
descendNoStore
(*node, i);
265
266
if
(nextNode)
267
{
268
if
(nextNode->isInner())
269
nodeStack.
push
(
intr_ptr::staticPointerCast<SHAMapInnerNode>
(nextNode));
270
}
271
else
272
{
273
missingNodes.
emplace_back
(
type_
, node->getChildHash(i));
274
if
(--maxMissing <= 0)
275
return
;
276
}
277
}
278
}
279
}
280
}
281
282
bool
283
SHAMap::walkMapParallel
(
std::vector<SHAMapMissingNode>
& missingNodes,
int
maxMissing)
const
284
{
285
if
(!
root_
->isInner())
// root_ is only node, and we have it
286
return
false
;
287
288
using
StackEntry =
intr_ptr::SharedPtr<SHAMapInnerNode>
;
289
std::array<SHAMapTreeNodePtr, SHAMapInnerNode::kBranchFactor>
topChildren;
290
{
291
auto
const
& innerRoot =
intr_ptr::staticPointerCast<SHAMapInnerNode>
(
root_
);
292
for
(
auto
i = 0u; i <
SHAMapInnerNode::kBranchFactor
; ++i)
293
{
294
if
(!innerRoot->isEmptyBranch(i))
295
topChildren[i] =
descendNoStore
(*innerRoot, i);
296
}
297
}
298
std::vector<std::thread>
workers;
299
workers.
reserve
(
SHAMapInnerNode::kBranchFactor
);
300
std::vector<SHAMapMissingNode>
exceptions;
301
exceptions.
reserve
(
SHAMapInnerNode::kBranchFactor
);
302
303
std::array<std::stack<StackEntry, std::vector<StackEntry>
>,
SHAMapInnerNode::kBranchFactor
>
304
nodeStacks;
305
306
// This mutex is used inside the worker threads to protect `missingNodes`
307
// and `maxMissing` from race conditions
308
std::mutex
m;
309
310
for
(
auto
rootChildIndex = 0u; rootChildIndex <
SHAMapInnerNode::kBranchFactor
;
311
++rootChildIndex)
312
{
313
auto
const
& child = topChildren[rootChildIndex];
314
if
(!child || !child->isInner())
315
continue
;
316
317
nodeStacks[rootChildIndex].push(
intr_ptr::staticPointerCast<SHAMapInnerNode>
(child));
318
319
JLOG(
journal_
.debug()) <<
"starting worker "
<< rootChildIndex;
320
workers.
emplace_back
(
321
[&m, &missingNodes, &maxMissing, &exceptions,
this
](
322
std::stack
<StackEntry,
std::vector<StackEntry>
> nodeStack) {
323
try
324
{
325
while
(!nodeStack.empty())
326
{
327
intr_ptr::SharedPtr<SHAMapInnerNode>
const
node =
328
std::move(nodeStack.top());
329
XRPL_ASSERT(node,
"xrpl::SHAMap::walkMapParallel : non-null node"
);
330
nodeStack.pop();
331
332
for
(
auto
i = 0u; i <
SHAMapInnerNode::kBranchFactor
; ++i)
333
{
334
if
(node->isEmptyBranch(i))
335
continue
;
336
SHAMapTreeNodePtr
const
nextNode =
descendNoStore
(*node, i);
337
338
if
(nextNode)
339
{
340
if
(nextNode->isInner())
341
{
342
nodeStack.push(
343
intr_ptr::staticPointerCast<SHAMapInnerNode>
(nextNode));
344
}
345
}
346
else
347
{
348
std::scoped_lock
const
l{m};
349
missingNodes.
emplace_back
(
type_
, node->getChildHash(i));
350
if
(--maxMissing <= 0)
351
return
;
352
}
353
}
354
}
355
}
356
catch
(
SHAMapMissingNode
const
& e)
357
{
358
std::scoped_lock
const
l(m);
359
exceptions.
push_back
(e);
360
}
361
},
362
std::move(nodeStacks[rootChildIndex]));
363
}
364
365
for
(
std::thread
& worker : workers)
366
worker.join();
367
368
std::scoped_lock
const
l(m);
369
if
(exceptions.
empty
())
370
return
true
;
371
std::stringstream
ss;
372
ss <<
"Exception(s) in ledger load: "
;
373
for
(
auto
const
& e : exceptions)
374
ss << e.what() <<
", "
;
375
JLOG(
journal_
.error()) << ss.
str
();
376
return
false
;
377
}
378
379
}
// namespace xrpl
array
std::stringstream
xrpl::SHAMapInnerNode::kBranchFactor
static constexpr unsigned int kBranchFactor
Each inner node has 16 children (the 'radix tree' part of the map).
Definition
SHAMapInnerNode.h:24
xrpl::SHAMapMissingNode
Definition
SHAMapMissingNode.h:36
xrpl::SHAMapTreeNode
Definition
SHAMapTreeNode.h:38
xrpl::SHAMapTreeNode::isInner
virtual bool isInner() const =0
Determines if this is an inner node.
xrpl::SHAMap::descendNoStore
SHAMapTreeNodePtr descendNoStore(SHAMapInnerNode &, unsigned int branch) const
Definition
libxrpl/shamap/SHAMap.cpp:339
xrpl::SHAMap::DeltaRef
std::pair< boost::intrusive_ptr< SHAMapItem const >, boost::intrusive_ptr< SHAMapItem const > > DeltaRef
Definition
SHAMap.h:524
xrpl::SHAMap::walkMapParallel
bool walkMapParallel(std::vector< SHAMapMissingNode > &missingNodes, int maxMissing) const
Definition
SHAMapDelta.cpp:283
xrpl::SHAMap::walkMap
void walkMap(std::vector< SHAMapMissingNode > &missingNodes, int maxMissing) const
Definition
SHAMapDelta.cpp:245
xrpl::SHAMap::journal_
beast::Journal journal_
Definition
SHAMap.h:116
xrpl::SHAMap::Delta
std::map< UInt256, DeltaItem > Delta
Definition
SHAMap.h:148
xrpl::SHAMap::compare
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
Definition
SHAMapDelta.cpp:130
xrpl::SHAMap::descendThrow
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, unsigned int branch) const
Definition
libxrpl/shamap/SHAMap.cpp:285
xrpl::SHAMap::isValid
bool isValid() const
Definition
SHAMap.h:779
xrpl::SHAMap::type_
SHAMapType const type_
Definition
SHAMap.h:130
xrpl::SHAMap::SHAMap
SHAMap()=delete
xrpl::SHAMap::root_
SHAMapTreeNodePtr root_
Definition
SHAMap.h:128
xrpl::SHAMap::getHash
SHAMapHash getHash() const
Definition
libxrpl/shamap/SHAMap.cpp:795
xrpl::SHAMap::walkBranch
bool walkBranch(SHAMapTreeNode *node, boost::intrusive_ptr< SHAMapItem const > const &otherMapItem, bool isFirstMap, Delta &differences, int &maxCount) const
Definition
SHAMapDelta.cpp:34
xrpl::SharedIntrusive::get
T * get() const
Get the raw pointer.
std::vector::emplace_back
T emplace_back(T... args)
std::stack::emplace
T emplace(T... args)
std::stack::empty
T empty(T... args)
std::map::insert
T insert(T... args)
std::make_pair
T make_pair(T... args)
mutex
xrpl::intr_ptr::staticPointerCast
SharedPtr< T > staticPointerCast(TT const &v)
Definition
IntrusivePointer.h:527
xrpl::intr_ptr::SharedPtr
SharedIntrusive< T > SharedPtr
Definition
IntrusivePointer.h:510
xrpl
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition
algorithm.h:5
xrpl::SHAMapTreeNodePtr
intr_ptr::SharedPtr< SHAMapTreeNode > SHAMapTreeNodePtr
Definition
SHAMapTreeNode.h:17
xrpl::UInt256
BaseUInt< 256 > UInt256
Definition
base_uint.h:580
xrpl::safeDowncast
Dest safeDowncast(Src *s) noexcept
Definition
safe_cast.h:84
xrpl::Throw
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition
contract.h:52
std::pair
std::stack::pop
T pop(T... args)
std::vector::push_back
T push_back(T... args)
std::stack::push
T push(T... args)
std::vector::reserve
T reserve(T... args)
std::scoped_lock
sstream
stack
std::stringstream::str
T str(T... args)
thread
std::stack::top
T top(T... args)
utility
vector
Generated by
1.17.0