xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
libxrpl
shamap
libxrpl/shamap/SHAMapNodeID.cpp
1
#include <xrpl/shamap/SHAMapNodeID.h>
2
3
#include <xrpl/basics/base_uint.h>
4
#include <xrpl/basics/contract.h>
5
#include <xrpl/beast/utility/instrumentation.h>
6
#include <xrpl/protocol/Serializer.h>
7
#include <xrpl/shamap/SHAMap.h>
8
9
#include <
algorithm
>
10
#include <
cstddef
>
11
#include <format>
12
#include <
optional
>
13
#include <
stdexcept
>
14
#include <
string
>
15
16
namespace
xrpl
{
17
18
static
UInt256
const
&
19
depthMask
(
unsigned
int
depth)
20
{
21
static
constexpr
auto
kMaskSize =
SHAMap::kLeafDepth
+ 1;
22
23
struct
MasksT
24
{
25
UInt256
entry[kMaskSize];
26
27
MasksT()
28
{
29
UInt256
selector;
30
for
(
auto
i = 0u; i < kMaskSize - 1; i += 2)
31
{
32
entry[i] = selector;
33
*(selector.
begin
() + (i / 2)) = 0xF0;
34
entry[i + 1] = selector;
35
*(selector.
begin
() + (i / 2)) = 0xFF;
36
}
37
entry[kMaskSize - 1] = selector;
38
}
39
};
40
41
static
MasksT
const
kMasks;
42
return
kMasks.entry[depth];
43
}
44
45
// The prefix of `key` at `depth`: the leading nibbles naming the subtree a node at that depth
46
// identifies, with the remainder of the key masked off.
47
static
UInt256
48
maskedToDepth
(
UInt256
const
& key,
unsigned
int
depth)
49
{
50
return
key &
depthMask
(depth);
51
}
52
53
// Whether `id` at `depth` is what `key` looks like once masked down to that depth, i.e.
54
// whether an ID with this depth and id names a subtree that `key` falls under.
55
static
bool
56
isPrefixOfAtDepth
(
UInt256
const
&
id
,
unsigned
int
depth,
UInt256
const
& key)
57
{
58
return
maskedToDepth
(key, depth) == id;
59
}
60
61
// canonicalize the hash to a node ID for this depth
62
SHAMapNodeID::SHAMapNodeID
(
unsigned
int
depth,
UInt256
const
& hash) :
id_
(hash),
depth_
(depth)
63
{
64
// Every SHAMapNodeID's depth is stored here, so this is the one place that can stop an
65
// out-of-range one from being kept: a depth past kLeafDepth would go on to index depthMask
66
// out of bounds, and getRawString would narrow it to a byte, silently renaming the node.
67
// Clamp rather than throw, since node IDs are built from peer-supplied depths on the ledger
68
// data path, where no caller catches an exception before it reaches a thread boundary.
69
if
(
depth_
>
SHAMap::kLeafDepth
)
70
{
71
// LCOV_EXCL_START
72
UNREACHABLE(
"xrpl::SHAMapNodeID::SHAMapNodeID : depth within tree"
);
73
depth_
=
SHAMap::kLeafDepth
;
74
id_
=
maskedToDepth
(
id_
,
depth_
);
75
// LCOV_EXCL_STOP
76
}
77
78
// Reads the clamped member rather than the depth argument, so it cannot index depthMask past
79
// its last entry even once the clamp above has reported the bad input and carried on.
80
XRPL_ASSERT(
81
isPrefixOf
(
id_
),
"xrpl::SHAMapNodeID::SHAMapNodeID : hash and depth inputs do match"
);
82
}
83
84
std::string
85
SHAMapNodeID::getRawString
()
const
86
{
87
Serializer
s(33);
88
s.
addBitString
(
id_
);
89
s.
add8
(
depth_
);
90
return
s.
getString
();
91
}
92
93
SHAMapNodeID
94
SHAMapNodeID::getChildNodeID
(
unsigned
int
branch)
const
95
{
96
XRPL_ASSERT(
97
branch <
SHAMap::kBranchFactor
,
"xrpl::SHAMapNodeID::getChildNodeID : valid branch input"
);
98
99
// A SHAMap has exactly 65 levels, so nodes must not exceed that
100
// depth; if they do, this breaks the invariant of never allowing
101
// the construction of a SHAMapNodeID at an invalid depth. We assert
102
// to catch this in debug builds.
103
//
104
// We throw (but never assert) if the node is at level 64, since
105
// entries at that depth are leaf nodes and have no children and even
106
// constructing a child node from them would break the above invariant.
107
XRPL_ASSERT(
108
depth_
<=
SHAMap::kLeafDepth
,
"xrpl::SHAMapNodeID::getChildNodeID : maximum leaf depth"
);
109
110
if
(
depth_
>=
SHAMap::kLeafDepth
)
111
Throw<std::logic_error>
(
std::format
(
"Request for child node ID of {}"
,
to_string
(*
this
)));
112
113
if
(!
isPrefixOf
(
id_
))
114
Throw<std::logic_error>
(
std::format
(
"Incorrect mask for {}"
,
to_string
(*
this
)));
115
116
SHAMapNodeID
node{
depth_
+ 1,
id_
};
117
node.
id_
.
begin
()[
depth_
/ 2] |= ((
depth_
& 1) != 0u) ? branch : (branch << 4);
118
return
node;
119
}
120
121
bool
122
SHAMapNodeID::isPrefixOf
(
UInt256
const
& key)
const
123
{
124
return
isPrefixOfAtDepth
(
id_
,
depth_
, key);
125
}
126
127
[[nodiscard]]
std::optional<SHAMapNodeID>
128
deserializeSHAMapNodeID
(
void
const
* data,
std::size_t
size)
129
{
130
std::optional<SHAMapNodeID>
ret;
131
132
if
(size == 33)
133
{
134
unsigned
int
const
depth = *(
static_cast<
unsigned
char
const*
>
(data) + 32);
135
if
(depth <=
SHAMap::kLeafDepth
)
136
{
137
// Reject a serialized ID carrying bits below its own depth. Checked before
138
// constructing, since the constructor asserts that same property.
139
if
(
auto
const
id
=
UInt256::fromVoid
(data);
isPrefixOfAtDepth
(
id
, depth,
id
))
140
ret.
emplace
(depth,
id
);
141
}
142
}
143
144
return
ret;
145
}
146
147
[[nodiscard]]
unsigned
int
148
selectBranch
(
SHAMapNodeID
const
&
id
,
UInt256
const
& hash)
149
{
150
XRPL_ASSERT(
id
.getDepth() <
SHAMap::kLeafDepth
,
"xrpl::selectBranch : depth below leaf depth"
);
151
152
// A depth-64 ID has no nibble left to select. Callers must not ask, but clamp anyway to keep
153
// the read below the end of the 32-byte key.
154
auto
const
depth =
std::min
(
id
.getDepth(),
SHAMap::kLeafDepth
- 1u);
155
auto
branch =
static_cast<
unsigned
int
>
(*(hash.
begin
() + (depth / 2)));
156
157
if
((depth & 1) != 0u)
158
{
159
branch &= 0xf;
160
}
161
else
162
{
163
branch >>= 4;
164
}
165
166
XRPL_ASSERT(branch <
SHAMap::kBranchFactor
,
"xrpl::selectBranch : maximum result"
);
167
return
branch;
168
}
169
170
SHAMapNodeID
171
SHAMapNodeID::createID
(
unsigned
int
depth,
UInt256
const
& key)
172
{
173
// The mask is chosen here, before the constructor runs, so the clamp there cannot cover this
174
// call: an out-of-range depth would index depthMask's table while still evaluating this
175
// argument. A public factory has to hold its own bound.
176
if
(depth >
SHAMap::kLeafDepth
)
177
{
178
// LCOV_EXCL_START
179
UNREACHABLE(
"xrpl::SHAMapNodeID::createID : depth within tree"
);
180
depth =
SHAMap::kLeafDepth
;
181
// LCOV_EXCL_STOP
182
}
183
184
return
SHAMapNodeID
(depth,
maskedToDepth
(key, depth));
185
}
186
187
}
// namespace xrpl
algorithm
std::string
xrpl::BaseUInt< 256 >::fromVoid
static BaseUInt fromVoid(void const *data)
Definition
base_uint.h:339
xrpl::BaseUInt::begin
iterator begin()
Definition
base_uint.h:128
xrpl::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition
SHAMapNodeID.h:20
xrpl::SHAMapNodeID::isPrefixOf
bool isPrefixOf(UInt256 const &key) const
Test whether this node ID lies on the path to the given leaf key.
Definition
libxrpl/shamap/SHAMapNodeID.cpp:122
xrpl::SHAMapNodeID::getChildNodeID
SHAMapNodeID getChildNodeID(unsigned int branch) const
Definition
libxrpl/shamap/SHAMapNodeID.cpp:94
xrpl::SHAMapNodeID::SHAMapNodeID
SHAMapNodeID()=default
xrpl::SHAMapNodeID::depth_
unsigned int depth_
Definition
SHAMapNodeID.h:23
xrpl::SHAMapNodeID::createID
static SHAMapNodeID createID(unsigned int depth, UInt256 const &key)
Create a SHAMapNodeID of a node with the depth of the node and the key of a leaf.
Definition
libxrpl/shamap/SHAMapNodeID.cpp:171
xrpl::SHAMapNodeID::id_
UInt256 id_
Definition
SHAMapNodeID.h:22
xrpl::SHAMapNodeID::getRawString
std::string getRawString() const
Definition
libxrpl/shamap/SHAMapNodeID.cpp:85
xrpl::SHAMap::kLeafDepth
static constexpr unsigned int kLeafDepth
The depth of the hash map: data is only present in the leaves.
Definition
SHAMap.h:144
xrpl::SHAMap::kBranchFactor
static constexpr unsigned int kBranchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map).
Definition
SHAMap.h:139
xrpl::Serializer
Definition
Serializer.h:23
xrpl::Serializer::addBitString
int addBitString(BaseUInt< Bits, Tag > const &v)
Definition
Serializer.h:202
xrpl::Serializer::getString
std::string getString() const
Definition
Serializer.h:309
xrpl::Serializer::add8
int add8(unsigned char byteValue)
Definition
libxrpl/protocol/Serializer.cpp:146
cstddef
std::optional::emplace
T emplace(T... args)
std::format
T format(T... args)
std::min
T min(T... args)
xrpl
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition
algorithm.h:5
xrpl::maskedToDepth
static UInt256 maskedToDepth(UInt256 const &key, unsigned int depth)
Definition
libxrpl/shamap/SHAMapNodeID.cpp:48
xrpl::isPrefixOfAtDepth
static bool isPrefixOfAtDepth(UInt256 const &id, unsigned int depth, UInt256 const &key)
Definition
libxrpl/shamap/SHAMapNodeID.cpp:56
xrpl::to_string
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition
base_uint.h:657
xrpl::UInt256
BaseUInt< 256 > UInt256
Definition
base_uint.h:580
xrpl::depthMask
static UInt256 const & depthMask(unsigned int depth)
Definition
libxrpl/shamap/SHAMapNodeID.cpp:19
xrpl::deserializeSHAMapNodeID
std::optional< SHAMapNodeID > deserializeSHAMapNodeID(void const *data, std::size_t size)
Return an object representing a serialized SHAMap Node ID.
Definition
libxrpl/shamap/SHAMapNodeID.cpp:128
xrpl::selectBranch
unsigned int selectBranch(SHAMapNodeID const &id, UInt256 const &hash)
Returns the branch that would contain the given hash.
Definition
libxrpl/shamap/SHAMapNodeID.cpp:148
xrpl::Throw
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition
contract.h:52
optional
std::size_t
stdexcept
string
Generated by
1.17.0