xrpld
Loading...
Searching...
No Matches
RCLValidations_test.cpp
1
2#include <test/jtx/Env.h>
3
4#include <xrpld/app/consensus/RCLValidations.h>
5#include <xrpld/consensus/LedgerTrie.h>
6#include <xrpld/core/Config.h>
7
8#include <xrpl/basics/base_uint.h>
9#include <xrpl/basics/chrono.h>
10#include <xrpl/beast/unit_test/suite.h>
11#include <xrpl/ledger/Ledger.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/KeyType.h>
14#include <xrpl/protocol/PublicKey.h>
15#include <xrpl/protocol/SField.h>
16#include <xrpl/protocol/STValidation.h>
17#include <xrpl/protocol/SecretKey.h>
18
19#include <memory>
20#include <vector>
21
22namespace xrpl::test {
23
25{
26 void
28 {
29 testcase("Change validation trusted status");
33 keys.first,
34 keys.second,
35 calcNodeID(keys.first),
36 [&](STValidation& v) { v.setFieldU32(sfLedgerSequence, 123456); });
37
38 BEAST_EXPECT(v->isTrusted());
39 v->setUntrusted();
40 BEAST_EXPECT(!v->isTrusted());
41
42 RCLValidation rcv{v};
43 BEAST_EXPECT(!rcv.trusted());
44 rcv.setTrusted();
45 BEAST_EXPECT(rcv.trusted());
46 rcv.setUntrusted();
47 BEAST_EXPECT(!rcv.trusted());
48 }
49
50 void
52 {
53 testcase("RCLValidatedLedger ancestry");
54
56 using ID = RCLValidatedLedger::ID;
57
58 // This tests RCLValidatedLedger properly implements the type
59 // requirements of a LedgerTrie ledger, with its added behavior that
60 // only the 256 prior ledger hashes are available to determine ancestry.
61 Seq const maxAncestors = 256;
62
63 //----------------------------------------------------------------------
64 // Generate two ledger histories that agree on the first maxAncestors
65 // ledgers, then diverge.
66
68
69 jtx::Env env(*this);
70 Config const config;
73 Rules{config.features},
74 config.fees.toFees(),
76 env.app().getNodeFamily());
77 history.push_back(prev);
78 for (auto i = 0; i < ((2 * maxAncestors) + 1); ++i)
79 {
80 auto next = std::make_shared<Ledger>(*prev, env.app().getTimeKeeper().closeTime());
81 next->updateSkipList();
82 history.push_back(next);
83 prev = next;
84 }
85
86 // altHistory agrees with first half of regular history
87 Seq const diverge = history.size() / 2;
89 history.begin(), history.begin() + diverge);
90 // advance clock to get new ledgers
91 using namespace std::chrono_literals;
92 env.timeKeeper().set(env.timeKeeper().now() + 1200s);
93 prev = altHistory.back();
94 bool forceHash = true;
95 while (altHistory.size() < history.size())
96 {
97 auto next = std::make_shared<Ledger>(*prev, env.app().getTimeKeeper().closeTime());
98 // Force a different hash on the first iteration
99 next->updateSkipList();
100 BEAST_EXPECT(next->read(keylet::feeSettings()));
101 if (forceHash)
102 {
103 next->setImmutable();
104 forceHash = false;
105 }
106
107 altHistory.push_back(next);
108 prev = next;
109 }
110
111 //----------------------------------------------------------------------
112
113 // Empty ledger
114 {
116 BEAST_EXPECT(a.seq() == Seq{0});
117 BEAST_EXPECT(a[Seq{0}] == ID{0});
118 BEAST_EXPECT(a.minSeq() == Seq{0});
119 }
120
121 // Full history ledgers
122 {
123 std::shared_ptr<Ledger const> const ledger = history.back();
124 RCLValidatedLedger const a{ledger, env.journal};
125 BEAST_EXPECT(a.seq() == ledger->header().seq);
126 BEAST_EXPECT(a.minSeq() == a.seq() - maxAncestors);
127 // Ensure the ancestral 256 ledgers have proper ID
128 for (Seq s = a.seq(); s > 0; s--)
129 {
130 if (s >= a.minSeq())
131 {
132 BEAST_EXPECT(a[s] == history[s - 1]->header().hash);
133 }
134 else
135 {
136 BEAST_EXPECT(a[s] == ID{0});
137 }
138 }
139 }
140
141 // Mismatch tests
142
143 // Empty with non-empty
144 {
146
147 for (auto const& ledger : {history.back(), history[maxAncestors - 1]})
148 {
149 RCLValidatedLedger const b{ledger, env.journal};
150 BEAST_EXPECT(mismatch(a, b) == 1);
151 BEAST_EXPECT(mismatch(b, a) == 1);
152 }
153 }
154 // Same chains, different seqs
155 {
156 RCLValidatedLedger const a{history.back(), env.journal};
157 for (Seq s = a.seq(); s > 0; s--)
158 {
159 RCLValidatedLedger const b{history[s - 1], env.journal};
160 if (s >= a.minSeq())
161 {
162 BEAST_EXPECT(mismatch(a, b) == b.seq() + 1);
163 BEAST_EXPECT(mismatch(b, a) == b.seq() + 1);
164 }
165 else
166 {
167 BEAST_EXPECT(mismatch(a, b) == Seq{1});
168 BEAST_EXPECT(mismatch(b, a) == Seq{1});
169 }
170 }
171 }
172 // Different chains, same seqs
173 {
174 // Alt history diverged at history.size()/2
175 for (Seq s = 1; s < history.size(); ++s)
176 {
177 RCLValidatedLedger const a{history[s - 1], env.journal};
178 RCLValidatedLedger const b{altHistory[s - 1], env.journal};
179
180 BEAST_EXPECT(a.seq() == b.seq());
181 if (s <= diverge)
182 {
183 BEAST_EXPECT(a[a.seq()] == b[b.seq()]);
184 BEAST_EXPECT(mismatch(a, b) == a.seq() + 1);
185 BEAST_EXPECT(mismatch(b, a) == a.seq() + 1);
186 }
187 else
188 {
189 BEAST_EXPECT(a[a.seq()] != b[b.seq()]);
190 BEAST_EXPECT(mismatch(a, b) == diverge + 1);
191 BEAST_EXPECT(mismatch(b, a) == diverge + 1);
192 }
193 }
194 }
195 // Different chains, different seqs
196 {
197 // Compare around the divergence point
198 RCLValidatedLedger const a{history[diverge], env.journal};
199 for (Seq offset = diverge / 2; offset < 3 * diverge / 2; ++offset)
200 {
201 RCLValidatedLedger const b{altHistory[offset - 1], env.journal};
202 if (offset <= diverge)
203 {
204 BEAST_EXPECT(mismatch(a, b) == b.seq() + 1);
205 }
206 else
207 {
208 BEAST_EXPECT(mismatch(a, b) == diverge + 1);
209 }
210 }
211 }
212 }
213
214 void
216 {
217 testcase("RCLValidatedLedger LedgerTrie");
218
219 // This test exposes an issue with the limited 256
220 // ancestor hash design of RCLValidatedLedger.
221 // There is only a single chain of validated ledgers
222 // but the 256 gap causes a "split" in the LedgerTrie
223 // due to the lack of ancestry information for a later ledger.
224 // This exposes a bug in which we are unable to remove
225 // support for a ledger hash which is already in the trie.
226
228
229 // Max known ancestors for each ledger
230 Seq const maxAncestors = 256;
232
233 // Generate a chain of 256 + 10 ledgers
234 jtx::Env env(*this);
235 auto& j = env.journal;
236 Config const config;
239 Rules{config.features},
240 config.fees.toFees(),
242 env.app().getNodeFamily());
243 history.push_back(prev);
244 for (auto i = 0; i < (maxAncestors + 10); ++i)
245 {
246 auto next = std::make_shared<Ledger>(*prev, env.app().getTimeKeeper().closeTime());
247 next->updateSkipList();
248 history.push_back(next);
249 prev = next;
250 }
251
253
254 // First, create the single branch trie, with ledgers
255 // separated by exactly 256 ledgers
256 auto ledg002 = RCLValidatedLedger{history[1], j};
257 auto ledg258 = RCLValidatedLedger{history[257], j};
258 auto ledg259 = RCLValidatedLedger{history[258], j};
259
260 trie.insert(ledg002);
261 trie.insert(ledg258, 4);
262 // trie.dump(std::cout);
263 // 000000[0,1)(T:0,B:5)
264 // |-AB868A..36C8[1,3)(T:1,B:5)
265 // |-AB868A..37C8[3,259)(T:4,B:4)
266 BEAST_EXPECT(trie.tipSupport(ledg002) == 1);
267 BEAST_EXPECT(trie.branchSupport(ledg002) == 5);
268 BEAST_EXPECT(trie.tipSupport(ledg258) == 4);
269 BEAST_EXPECT(trie.branchSupport(ledg258) == 4);
270
271 // Move three of the s258 ledgers to s259, which splits the trie
272 // due to the 256 ancestry limit
273 BEAST_EXPECT(trie.remove(ledg258, 3));
274 trie.insert(ledg259, 3);
275 [[maybe_unused]] auto unused1 = trie.getPreferred(1);
276 // trie.dump(std::cout);
277 // 000000[0,1)(T:0,B:5)
278 // |-AB868A..37C9[1,260)(T:3,B:3)
279 // |-AB868A..36C8[1,3)(T:1,B:2)
280 // |-AB868A..37C8[3,259)(T:1,B:1)
281 BEAST_EXPECT(trie.tipSupport(ledg002) == 1);
282 BEAST_EXPECT(trie.branchSupport(ledg002) == 2);
283 BEAST_EXPECT(trie.tipSupport(ledg258) == 1);
284 BEAST_EXPECT(trie.branchSupport(ledg258) == 1);
285 BEAST_EXPECT(trie.tipSupport(ledg259) == 3);
286 BEAST_EXPECT(trie.branchSupport(ledg259) == 3);
287
288 // The last call to trie.getPreferred cycled the children of the root
289 // node to make the new branch the first child (since it has support 3)
290 // then verify the remove call works
291 // past bug: remove had assumed the first child of a node in the trie
292 // which matches is the *only* child in the trie which matches.
293 // This is **NOT** true with the limited 256 ledger ancestry
294 // quirk of RCLValidation and prevents deleting the old support
295 // for ledger 257
296
297 BEAST_EXPECT(trie.remove(RCLValidatedLedger{history[257], env.journal}, 1));
298 trie.insert(RCLValidatedLedger{history[258], env.journal}, 1);
299 [[maybe_unused]] auto unused2 = trie.getPreferred(1);
300 // trie.dump(std::cout);
301 // 000000[0,1)(T:0,B:5)
302 // |-AB868A..37C9[1,260)(T:4,B:4)
303 // |-AB868A..36C8[1,3)(T:1,B:1)
304 BEAST_EXPECT(trie.tipSupport(ledg002) == 1);
305 BEAST_EXPECT(trie.branchSupport(ledg002) == 1);
306 BEAST_EXPECT(trie.tipSupport(ledg258) == 0);
307 // 258 no longer lives on a tip in the tree, BUT it is an ancestor
308 // of 259 which is a tip and therefore gets it's branchSupport value
309 // implicitly
310 BEAST_EXPECT(trie.branchSupport(ledg258) == 4);
311 BEAST_EXPECT(trie.tipSupport(ledg259) == 4);
312 BEAST_EXPECT(trie.branchSupport(ledg259) == 4);
313 }
314
315public:
316 void
323};
324
326
327} // namespace xrpl::test
T back(T... args)
T begin(T... args)
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
std::unordered_set< uint256, beast::Uhash<> > features
Definition Config.h:261
FeeSetup fees
Definition Config.h:189
Ancestry trie of ledgers.
Definition LedgerTrie.h:323
std::uint32_t tipSupport(Ledger const &ledger) const
Return count of tip support for the specific ledger.
Definition LedgerTrie.h:569
std::uint32_t branchSupport(Ledger const &ledger) const
Return the count of branch support for the specific ledger.
Definition LedgerTrie.h:583
std::optional< SpanTip< Ledger > > getPreferred(Seq const largestIssued) const
Return the preferred ledger ID.
Definition LedgerTrie.h:657
void insert(Ledger const &ledger, std::uint32_t count=1)
Insert and/or increment the support for the given ledger.
Definition LedgerTrie.h:425
bool remove(Ledger const &ledger, std::uint32_t count=1)
Decrease support for a ledger, removing and compressing if possible.
Definition LedgerTrie.h:511
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
Wraps a ledger instance for use in generic Validations LedgerTrie.
Seq seq() const
The sequence (index) of the ledger.
Wrapper over STValidation for generic Validation code.
bool trusted() const
Whether the validation is considered trusted.
Rules controlling protocol behavior.
Definition Rules.h:33
virtual TimeKeeper & getTimeKeeper()=0
time_point closeTime() const
Returns the predicted close time, in network time.
Definition TimeKeeper.h:56
time_point now() const override
Returns the current time.
void run() override
Runs the suite.
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
ManualTimeKeeper & timeKeeper()
Definition Env.h:293
beast::Journal const journal
Definition Env.h:184
T make_shared(T... args)
Keylet const & feeSettings() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:221
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
CreateGenesisT const kCreateGenesis
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
Validations< RCLValidationsAdaptor > RCLValidations
Alias for RCL-specific instantiation of generic Validations.
RCLValidatedLedger::Seq mismatch(RCLValidatedLedger const &a, RCLValidatedLedger const &b)
T push_back(T... args)
T size(T... args)
Fees toFees() const
Convert to a Fees object for use with Ledger construction.
Definition Config.h:64
Set the sequence number on a JTx.
Definition seq.h:12