xrpld
Loading...
Searching...
No Matches
LedgerNodeHelpers_test.cpp
1#include <xrpld/app/ledger/LedgerNodeHelpers.h>
2
3#include <xrpl/basics/IntrusivePointer.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/unit_test/suite.h>
6#include <xrpl/protocol/Serializer.h>
7#include <xrpl/shamap/SHAMap.h>
8#include <xrpl/shamap/SHAMapAccountStateLeafNode.h>
9#include <xrpl/shamap/SHAMapInnerNode.h>
10#include <xrpl/shamap/SHAMapItem.h>
11#include <xrpl/shamap/SHAMapNodeID.h>
12#include <xrpl/shamap/SHAMapTreeNode.h>
13
14#include <boost/smart_ptr/intrusive_ptr.hpp>
15
16#include <xrpl.pb.h>
17
18#include <cstdint>
19#include <string>
20
21namespace xrpl::tests {
22
24{
25 static boost::intrusive_ptr<SHAMapItem>
27 {
28 Serializer s;
29 s.add32(seed);
30 s.add32(seed + 1);
31 s.add32(seed + 2);
32 return makeShamapitem(s.getSHA512Half(), s.slice());
33 }
34
35 static std::string
37 {
38 Serializer s;
39 node->serializeForWire(s);
40 auto const slice = s.slice();
41 return std::string(slice.begin(), slice.end());
42 }
43
44 void
46 {
47 testcase("getTreeNode");
48
49 // Valid: inner node. It must have at least one child for `serializeNode` to work.
50 {
51 auto const innerNode = intr_ptr::makeShared<SHAMapInnerNode>(1);
52 auto const childNode = intr_ptr::makeShared<SHAMapInnerNode>(1);
53 innerNode->setChild(0, childNode);
54 auto const innerData = serializeNode(innerNode);
55 auto const result = getTreeNode(innerData);
56 BEAST_EXPECT(result && result->isInner());
57 }
58
59 // Valid: leaf node.
60 {
61 auto const leafItem = makeTestItem(12345);
62 auto const leafNode = intr_ptr::makeShared<SHAMapAccountStateLeafNode>(leafItem, 1);
63 auto const leafData = serializeNode(leafNode);
64 auto const result = getTreeNode(leafData);
65 BEAST_EXPECT(result && result->isLeaf());
66 }
67
68 // Invalid: empty data.
69 {
70 auto const result = getTreeNode("");
71 BEAST_EXPECT(!result);
72 }
73
74 // Invalid: garbage data.
75 {
76 auto const result = getTreeNode("invalid");
77 BEAST_EXPECT(!result);
78 }
79
80 // Invalid: truncated data.
81 {
82 auto const leafItem = makeTestItem(54321);
83 auto const leafNode = intr_ptr::makeShared<SHAMapAccountStateLeafNode>(leafItem, 1);
84 // Truncate the data to trigger an exception in SHAMapTreeNode::makeAccountState when
85 // the data is used to deserialize the node.
86 UInt256 const tag;
87 auto const leafData = serializeNode(leafNode).substr(0, tag.kBytes - 1);
88 auto const result = getTreeNode(leafData);
89 BEAST_EXPECT(!result);
90 }
91 }
92
93 void
95 {
96 testcase("getSHAMapNodeID");
97
98 {
99 // Tests using inner nodes at various depths.
100 auto const innerNode = intr_ptr::makeShared<SHAMapInnerNode>(1);
101 auto const childNode = intr_ptr::makeShared<SHAMapInnerNode>(1);
102 innerNode->setChild(0, childNode);
103 auto const innerData = serializeNode(innerNode);
104
105 // Valid: legacy `nodeid` field at arbitrary depth.
106 {
107 auto const innerDepth = 3;
108 auto const innerID = SHAMapNodeID::createID(innerDepth, UInt256{});
109
110 protocol::TMLedgerNode ledgerNode;
111 ledgerNode.set_nodedata(innerData);
112 ledgerNode.set_nodeid(innerID.getRawString());
113 auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
114 BEAST_EXPECT(result == innerID);
115 }
116
117 // Valid: new `id` field at minimum depth.
118 {
119 auto const innerDepth = 0;
120 auto const innerID = SHAMapNodeID::createID(innerDepth, UInt256{});
121
122 protocol::TMLedgerNode ledgerNode;
123 ledgerNode.set_nodedata(innerData);
124 ledgerNode.set_id(innerID.getRawString());
125 auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
126 BEAST_EXPECT(result == innerID);
127 }
128
129 // Invalid: new `depth` field should not be used for inner nodes.
130 {
131 protocol::TMLedgerNode ledgerNode;
132 ledgerNode.set_nodedata(innerData);
133 ledgerNode.set_depth(10);
134 auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
135 BEAST_EXPECT(!result);
136 }
137
138 // Invalid: both legacy `nodeid` and new `id` fields set for an inner node.
139 {
140 auto const innerDepth = 9;
141 auto const innerID = SHAMapNodeID::createID(innerDepth, UInt256{});
142
143 protocol::TMLedgerNode ledgerNode;
144 ledgerNode.set_nodedata(innerData);
145 ledgerNode.set_nodeid(innerID.getRawString());
146 ledgerNode.set_id(innerID.getRawString());
147 auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
148 BEAST_EXPECT(!result);
149 }
150 }
151
152 {
153 // Tests using leaf nodes at various depths.
154 auto const leafItem = makeTestItem(12345);
155 auto const leafNode = intr_ptr::makeShared<SHAMapAccountStateLeafNode>(leafItem, 1);
156 auto const leafData = serializeNode(leafNode);
157 auto const leafKey = leafItem->key();
158
159 // Valid: legacy `nodeid` field at arbitrary depth.
160 {
161 auto const kLeafDepth = 5;
162 auto const leafID = SHAMapNodeID::createID(kLeafDepth, leafKey);
163
164 protocol::TMLedgerNode ledgerNode;
165 ledgerNode.set_nodedata(leafData);
166 ledgerNode.set_nodeid(leafID.getRawString());
167 auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
168 BEAST_EXPECT(result == leafID);
169 }
170
171 // Invalid: new `id` field should not be used for leaf nodes.
172 {
173 auto const kLeafDepth = 5;
174 auto const leafID = SHAMapNodeID::createID(kLeafDepth, leafKey);
175
176 protocol::TMLedgerNode ledgerNode;
177 ledgerNode.set_nodedata(leafData);
178 ledgerNode.set_id(leafID.getRawString());
179 auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
180 BEAST_EXPECT(!result);
181 }
182
183 // Valid: new `depth` field at minimum depth.
184 {
185 auto const kLeafDepth = 0;
186 auto const leafID = SHAMapNodeID::createID(kLeafDepth, leafKey);
187
188 protocol::TMLedgerNode ledgerNode;
189 ledgerNode.set_nodedata(leafData);
190 ledgerNode.set_depth(kLeafDepth);
191 auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
192 BEAST_EXPECT(result == leafID);
193 }
194
195 // Valid: new `depth` field at arbitrary depth between minimum and maximum.
196 {
197 auto const kLeafDepth = 10;
198 auto const leafID = SHAMapNodeID::createID(kLeafDepth, leafKey);
199
200 protocol::TMLedgerNode ledgerNode;
201 ledgerNode.set_nodedata(leafData);
202 ledgerNode.set_depth(kLeafDepth);
203 auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
204 BEAST_EXPECT(result == leafID);
205 }
206
207 // Valid: new `depth` field at maximum depth.
208 // Note that we do not test a depth greater than the maximum depth, because the proto
209 // message is assumed to have been validated by the time the getSHAMapNodeID function is
210 // called.
211 {
212 auto const kLeafDepth = SHAMap::kLeafDepth;
213 auto const leafID = SHAMapNodeID::createID(kLeafDepth, leafKey);
214
215 protocol::TMLedgerNode ledgerNode;
216 ledgerNode.set_nodedata(leafData);
217 ledgerNode.set_depth(kLeafDepth);
218 auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
219 BEAST_EXPECT(result == leafID);
220 }
221
222 // Invalid: legacy `nodeid` field where the node ID is inconsistent with the key.
223 {
224 auto const otherItem = makeTestItem(54321);
225 auto const otherNode =
227 auto const otherData = serializeNode(otherNode);
228 auto const otherKey = otherItem->key();
229 auto const otherDepth = 1;
230 auto const otherID = SHAMapNodeID::createID(otherDepth, otherKey);
231
232 protocol::TMLedgerNode ledgerNode;
233 ledgerNode.set_nodedata(otherData);
234 ledgerNode.set_nodeid(otherID.getRawString());
235 auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
236 BEAST_EXPECT(!result);
237 }
238 }
239
240 // Invalid: no field set.
241 {
242 auto const innerNode = intr_ptr::makeShared<SHAMapInnerNode>(1);
243 protocol::TMLedgerNode ledgerNode;
244 ledgerNode.set_nodedata("test_data");
245 auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
246 BEAST_EXPECT(!result);
247 }
248 }
249
250public:
251 void
252 run() override
253 {
256 }
257};
258
259BEAST_DEFINE_TESTSUITE(LedgerNodeHelpers, app, xrpl);
260
261} // namespace xrpl::tests
A testsuite class.
Definition suite.h:52
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
static constexpr std::size_t kBytes
Definition base_uint.h:100
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.
static constexpr unsigned int kLeafDepth
The depth of the hash map: data is only present in the leaves.
Definition SHAMap.h:144
Slice slice() const noexcept
Definition Serializer.h:141
static boost::intrusive_ptr< SHAMapItem > makeTestItem(std::uint32_t seed)
static std::string serializeNode(SHAMapTreeNodePtr const &node)
SharedPtr< T > makeShared(A &&... args)
BEAST_DEFINE_TESTSUITE(LedgerNodeHelpers, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
intr_ptr::SharedPtr< SHAMapTreeNode > SHAMapTreeNodePtr
std::optional< SHAMapNodeID > getSHAMapNodeID(protocol::TMLedgerNode const &ledgerNode, SHAMapTreeNode const &treeNode)
Extracts or reconstructs the SHAMapNodeID from a ledger node proto message.
SHAMapTreeNodePtr getTreeNode(std::string_view data)
Deserializes a SHAMapTreeNode from wire format data.
BaseUInt< 256 > UInt256
Definition base_uint.h:580
boost::intrusive_ptr< SHAMapItem > makeShamapitem(UInt256 const &tag, Slice data)
Definition SHAMapItem.h:148
UInt256 const & leafKey(SHAMapTreeNode const &node)
Return the key of the item held by a SHAMap leaf node.
T substr(T... args)