rippled
Loading...
Searching...
No Matches
NegativeUNL_test.cpp
1//-----------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2020 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 <test/jtx.h>
21#include <xrpld/app/consensus/RCLValidations.h>
22#include <xrpld/app/ledger/Ledger.h>
23#include <xrpld/app/misc/NegativeUNLVote.h>
24#include <xrpld/app/misc/ValidatorList.h>
25#include <xrpld/app/tx/apply.h>
26#include <xrpld/ledger/View.h>
27#include <xrpl/basics/Log.h>
28#include <xrpl/beast/unit_test.h>
29
30namespace ripple {
31namespace test {
32
33/*
34 * This file implements the following negative UNL related tests:
35 * -- test filling and applying ttUNL_MODIFY Tx and ledger update
36 * -- test ttUNL_MODIFY Tx failure without featureNegativeUNL amendment
37 * -- test the NegativeUNLVote class. The test cases are split to multiple
38 * test classes to allow parallel execution.
39 * -- test the negativeUNLFilter function
40 *
41 * Other negative UNL related tests such as ValidatorList and RPC related ones
42 * are put in their existing unit test files.
43 */
44
55bool
58 size_t size,
59 bool hasToDisable,
60 bool hasToReEnable);
61
71bool
72applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass);
73
83bool
86 hash_map<PublicKey, std::uint32_t> nUnlLedgerSeq);
87
96
105
114STTx
115createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey);
116
118{
126 void
128 {
129 /*
130 * test cases:
131 *
132 * (1) the ledger after genesis
133 * -- cannot apply Disable Tx
134 * -- cannot apply ReEnable Tx
135 * -- nUNL empty
136 * -- no ToDisable
137 * -- no ToReEnable
138 *
139 * (2) a flag ledger
140 * -- apply an Disable Tx
141 * -- cannot apply the second Disable Tx
142 * -- cannot apply a ReEnable Tx
143 * -- nUNL empty
144 * -- has ToDisable with right nodeId
145 * -- no ToReEnable
146 * ++ extra test: first Disable Tx in ledger TxSet
147 *
148 * (3) ledgers before the next flag ledger
149 * -- nUNL empty
150 * -- has ToDisable with right nodeId
151 * -- no ToReEnable
152 *
153 * (4) next flag ledger
154 * -- nUNL size == 1, with right nodeId
155 * -- no ToDisable
156 * -- no ToReEnable
157 * -- cannot apply an Disable Tx with nodeId already in nUNL
158 * -- apply an Disable Tx with different nodeId
159 * -- cannot apply a ReEnable Tx with the same NodeId as Add
160 * -- cannot apply a ReEnable Tx with a NodeId not in nUNL
161 * -- apply a ReEnable Tx with a nodeId already in nUNL
162 * -- has ToDisable with right nodeId
163 * -- has ToReEnable with right nodeId
164 * -- nUNL size still 1, right nodeId
165 *
166 * (5) ledgers before the next flag ledger
167 * -- nUNL size == 1, right nodeId
168 * -- has ToDisable with right nodeId
169 * -- has ToReEnable with right nodeId
170 *
171 * (6) next flag ledger
172 * -- nUNL size == 1, different nodeId
173 * -- no ToDisable
174 * -- no ToReEnable
175 * -- apply an Disable Tx with different nodeId
176 * -- nUNL size still 1, right nodeId
177 * -- has ToDisable with right nodeId
178 * -- no ToReEnable
179 *
180 * (7) ledgers before the next flag ledger
181 * -- nUNL size still 1, right nodeId
182 * -- has ToDisable with right nodeId
183 * -- no ToReEnable
184 *
185 * (8) next flag ledger
186 * -- nUNL size == 2
187 * -- apply a ReEnable Tx
188 * -- cannot apply second ReEnable Tx, even with right nodeId
189 * -- cannot apply an Disable Tx with the same NodeId as Remove
190 * -- nUNL size == 2
191 * -- no ToDisable
192 * -- has ToReEnable with right nodeId
193 *
194 * (9) ledgers before the next flag ledger
195 * -- nUNL size == 2
196 * -- no ToDisable
197 * -- has ToReEnable with right nodeId
198 *
199 * (10) next flag ledger
200 * -- nUNL size == 1
201 * -- apply a ReEnable Tx
202 * -- nUNL size == 1
203 * -- no ToDisable
204 * -- has ToReEnable with right nodeId
205 *
206 * (11) ledgers before the next flag ledger
207 * -- nUNL size == 1
208 * -- no ToDisable
209 * -- has ToReEnable with right nodeId
210 *
211 * (12) next flag ledger
212 * -- nUNL size == 0
213 * -- no ToDisable
214 * -- no ToReEnable
215 *
216 * (13) ledgers before the next flag ledger
217 * -- nUNL size == 0
218 * -- no ToDisable
219 * -- no ToReEnable
220 *
221 * (14) next flag ledger
222 * -- nUNL size == 0
223 * -- no ToDisable
224 * -- no ToReEnable
225 */
226
227 testcase("Create UNLModify Tx and apply to ledgers");
228
229 jtx::Env env(*this, jtx::supported_amendments() | featureNegativeUNL);
231 // genesis ledger
232 auto l = std::make_shared<Ledger>(
234 env.app().config(),
236 env.app().getNodeFamily());
237 BEAST_EXPECT(l->rules().enabled(featureNegativeUNL));
238
239 // Record the public keys and ledger sequences of expected negative UNL
240 // validators when we build the ledger history
242
243 {
244 //(1) the ledger after genesis, not a flag ledger
245 l = std::make_shared<Ledger>(
246 *l, env.app().timeKeeper().closeTime());
247
248 auto txDisable_0 = createTx(true, l->seq(), publicKeys[0]);
249 auto txReEnable_1 = createTx(false, l->seq(), publicKeys[1]);
250
251 OpenView accum(&*l);
252 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_0, false));
253 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_1, false));
254 accum.apply(*l);
255 BEAST_EXPECT(negUnlSizeTest(l, 0, false, false));
256 }
257
258 {
259 //(2) a flag ledger
260 // generate more ledgers
261 for (auto i = 0; i < 256 - 2; ++i)
262 {
263 l = std::make_shared<Ledger>(
264 *l, env.app().timeKeeper().closeTime());
265 }
266 BEAST_EXPECT(l->isFlagLedger());
267 l->updateNegativeUNL();
268
269 auto txDisable_0 = createTx(true, l->seq(), publicKeys[0]);
270 auto txDisable_1 = createTx(true, l->seq(), publicKeys[1]);
271 auto txReEnable_2 = createTx(false, l->seq(), publicKeys[2]);
272
273 // can apply 1 and only 1 ToDisable Tx,
274 // cannot apply ToReEnable Tx, since negative UNL is empty
275 OpenView accum(&*l);
276 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_0, true));
277 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_1, false));
278 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_2, false));
279 accum.apply(*l);
280 auto good_size = negUnlSizeTest(l, 0, true, false);
281 BEAST_EXPECT(good_size);
282 if (good_size)
283 {
284 BEAST_EXPECT(l->validatorToDisable() == publicKeys[0]);
285 //++ first ToDisable Tx in ledger's TxSet
286 uint256 txID = txDisable_0.getTransactionID();
287 BEAST_EXPECT(l->txExists(txID));
288 }
289 }
290
291 {
292 //(3) ledgers before the next flag ledger
293 for (auto i = 0; i < 256; ++i)
294 {
295 auto good_size = negUnlSizeTest(l, 0, true, false);
296 BEAST_EXPECT(good_size);
297 if (good_size)
298 BEAST_EXPECT(l->validatorToDisable() == publicKeys[0]);
299 l = std::make_shared<Ledger>(
300 *l, env.app().timeKeeper().closeTime());
301 }
302 BEAST_EXPECT(l->isFlagLedger());
303 l->updateNegativeUNL();
304
305 //(4) next flag ledger
306 // test if the ledger updated correctly
307 auto good_size = negUnlSizeTest(l, 1, false, false);
308 BEAST_EXPECT(good_size);
309 if (good_size)
310 {
311 BEAST_EXPECT(*(l->negativeUNL().begin()) == publicKeys[0]);
312 nUnlLedgerSeq.emplace(publicKeys[0], l->seq());
313 }
314
315 auto txDisable_0 = createTx(true, l->seq(), publicKeys[0]);
316 auto txDisable_1 = createTx(true, l->seq(), publicKeys[1]);
317 auto txReEnable_0 = createTx(false, l->seq(), publicKeys[0]);
318 auto txReEnable_1 = createTx(false, l->seq(), publicKeys[1]);
319 auto txReEnable_2 = createTx(false, l->seq(), publicKeys[2]);
320
321 OpenView accum(&*l);
322 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_0, false));
323 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_1, true));
324 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_1, false));
325 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_2, false));
326 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_0, true));
327 accum.apply(*l);
328 good_size = negUnlSizeTest(l, 1, true, true);
329 BEAST_EXPECT(good_size);
330 if (good_size)
331 {
332 BEAST_EXPECT(l->negativeUNL().count(publicKeys[0]));
333 BEAST_EXPECT(l->validatorToDisable() == publicKeys[1]);
334 BEAST_EXPECT(l->validatorToReEnable() == publicKeys[0]);
335 // test sfFirstLedgerSequence
336 BEAST_EXPECT(VerifyPubKeyAndSeq(l, nUnlLedgerSeq));
337 }
338 }
339
340 {
341 //(5) ledgers before the next flag ledger
342 for (auto i = 0; i < 256; ++i)
343 {
344 auto good_size = negUnlSizeTest(l, 1, true, true);
345 BEAST_EXPECT(good_size);
346 if (good_size)
347 {
348 BEAST_EXPECT(l->negativeUNL().count(publicKeys[0]));
349 BEAST_EXPECT(l->validatorToDisable() == publicKeys[1]);
350 BEAST_EXPECT(l->validatorToReEnable() == publicKeys[0]);
351 }
352 l = std::make_shared<Ledger>(
353 *l, env.app().timeKeeper().closeTime());
354 }
355 BEAST_EXPECT(l->isFlagLedger());
356 l->updateNegativeUNL();
357
358 //(6) next flag ledger
359 // test if the ledger updated correctly
360 auto good_size = negUnlSizeTest(l, 1, false, false);
361 BEAST_EXPECT(good_size);
362 if (good_size)
363 {
364 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
365 }
366
367 auto txDisable_0 = createTx(true, l->seq(), publicKeys[0]);
368
369 OpenView accum(&*l);
370 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_0, true));
371 accum.apply(*l);
372 good_size = negUnlSizeTest(l, 1, true, false);
373 BEAST_EXPECT(good_size);
374 if (good_size)
375 {
376 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
377 BEAST_EXPECT(l->validatorToDisable() == publicKeys[0]);
378 nUnlLedgerSeq.emplace(publicKeys[1], l->seq());
379 nUnlLedgerSeq.erase(publicKeys[0]);
380 BEAST_EXPECT(VerifyPubKeyAndSeq(l, nUnlLedgerSeq));
381 }
382 }
383
384 {
385 //(7) ledgers before the next flag ledger
386 for (auto i = 0; i < 256; ++i)
387 {
388 auto good_size = negUnlSizeTest(l, 1, true, false);
389 BEAST_EXPECT(good_size);
390 if (good_size)
391 {
392 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
393 BEAST_EXPECT(l->validatorToDisable() == publicKeys[0]);
394 }
395 l = std::make_shared<Ledger>(
396 *l, env.app().timeKeeper().closeTime());
397 }
398 BEAST_EXPECT(l->isFlagLedger());
399 l->updateNegativeUNL();
400
401 //(8) next flag ledger
402 // test if the ledger updated correctly
403 auto good_size = negUnlSizeTest(l, 2, false, false);
404 BEAST_EXPECT(good_size);
405 if (good_size)
406 {
407 BEAST_EXPECT(l->negativeUNL().count(publicKeys[0]));
408 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
409 nUnlLedgerSeq.emplace(publicKeys[0], l->seq());
410 BEAST_EXPECT(VerifyPubKeyAndSeq(l, nUnlLedgerSeq));
411 }
412
413 auto txDisable_0 = createTx(true, l->seq(), publicKeys[0]);
414 auto txReEnable_0 = createTx(false, l->seq(), publicKeys[0]);
415 auto txReEnable_1 = createTx(false, l->seq(), publicKeys[1]);
416
417 OpenView accum(&*l);
418 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_0, true));
419 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_1, false));
420 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_0, false));
421 accum.apply(*l);
422 good_size = negUnlSizeTest(l, 2, false, true);
423 BEAST_EXPECT(good_size);
424 if (good_size)
425 {
426 BEAST_EXPECT(l->negativeUNL().count(publicKeys[0]));
427 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
428 BEAST_EXPECT(l->validatorToReEnable() == publicKeys[0]);
429 BEAST_EXPECT(VerifyPubKeyAndSeq(l, nUnlLedgerSeq));
430 }
431 }
432
433 {
434 //(9) ledgers before the next flag ledger
435 for (auto i = 0; i < 256; ++i)
436 {
437 auto good_size = negUnlSizeTest(l, 2, false, true);
438 BEAST_EXPECT(good_size);
439 if (good_size)
440 {
441 BEAST_EXPECT(l->negativeUNL().count(publicKeys[0]));
442 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
443 BEAST_EXPECT(l->validatorToReEnable() == publicKeys[0]);
444 }
445 l = std::make_shared<Ledger>(
446 *l, env.app().timeKeeper().closeTime());
447 }
448 BEAST_EXPECT(l->isFlagLedger());
449 l->updateNegativeUNL();
450
451 //(10) next flag ledger
452 // test if the ledger updated correctly
453 auto good_size = negUnlSizeTest(l, 1, false, false);
454 BEAST_EXPECT(good_size);
455 if (good_size)
456 {
457 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
458 nUnlLedgerSeq.erase(publicKeys[0]);
459 BEAST_EXPECT(VerifyPubKeyAndSeq(l, nUnlLedgerSeq));
460 }
461
462 auto txReEnable_1 = createTx(false, l->seq(), publicKeys[1]);
463
464 OpenView accum(&*l);
465 BEAST_EXPECT(applyAndTestResult(env, accum, txReEnable_1, true));
466 accum.apply(*l);
467 good_size = negUnlSizeTest(l, 1, false, true);
468 BEAST_EXPECT(good_size);
469 if (good_size)
470 {
471 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
472 BEAST_EXPECT(l->validatorToReEnable() == publicKeys[1]);
473 BEAST_EXPECT(VerifyPubKeyAndSeq(l, nUnlLedgerSeq));
474 }
475 }
476
477 {
478 //(11) ledgers before the next flag ledger
479 for (auto i = 0; i < 256; ++i)
480 {
481 auto good_size = negUnlSizeTest(l, 1, false, true);
482 BEAST_EXPECT(good_size);
483 if (good_size)
484 {
485 BEAST_EXPECT(l->negativeUNL().count(publicKeys[1]));
486 BEAST_EXPECT(l->validatorToReEnable() == publicKeys[1]);
487 }
488 l = std::make_shared<Ledger>(
489 *l, env.app().timeKeeper().closeTime());
490 }
491 BEAST_EXPECT(l->isFlagLedger());
492 l->updateNegativeUNL();
493
494 //(12) next flag ledger
495 BEAST_EXPECT(negUnlSizeTest(l, 0, false, false));
496 }
497
498 {
499 //(13) ledgers before the next flag ledger
500 for (auto i = 0; i < 256; ++i)
501 {
502 BEAST_EXPECT(negUnlSizeTest(l, 0, false, false));
503 l = std::make_shared<Ledger>(
504 *l, env.app().timeKeeper().closeTime());
505 }
506 BEAST_EXPECT(l->isFlagLedger());
507 l->updateNegativeUNL();
508
509 //(14) next flag ledger
510 BEAST_EXPECT(negUnlSizeTest(l, 0, false, false));
511 }
512 }
513
514 void
515 run() override
516 {
518 }
519};
520
522{
523 void
525 {
526 testcase("No negative UNL amendment");
527
528 jtx::Env env(*this, jtx::supported_amendments() - featureNegativeUNL);
530 // genesis ledger
531 auto l = std::make_shared<Ledger>(
533 env.app().config(),
535 env.app().getNodeFamily());
536 BEAST_EXPECT(!l->rules().enabled(featureNegativeUNL));
537
538 // generate more ledgers
539 for (auto i = 0; i < 256 - 1; ++i)
540 {
541 l = std::make_shared<Ledger>(
542 *l, env.app().timeKeeper().closeTime());
543 }
544 BEAST_EXPECT(l->seq() == 256);
545 auto txDisable_0 = createTx(true, l->seq(), publicKeys[0]);
546 OpenView accum(&*l);
547 BEAST_EXPECT(applyAndTestResult(env, accum, txDisable_0, false));
548 accum.apply(*l);
549 BEAST_EXPECT(negUnlSizeTest(l, 0, false, false));
550 }
551
552 void
553 run() override
554 {
556 }
557};
558
563{
571 {
572 std::uint32_t numNodes; // number of validators
573 std::uint32_t negUNLSize; // size of negative UNL in the last ledger
574 bool hasToDisable; // if has ToDisable in the last ledger
575 bool hasToReEnable; // if has ToReEnable in the last ledger
581 };
582
584 : env(suite, jtx::supported_amendments() | featureNegativeUNL)
585 , param(p)
586 , validations(env.app().getValidations())
587 {
588 createNodes();
589 if (!param.numLedgers)
590 param.numLedgers = 256 * (param.negUNLSize + 1);
592 }
593
594 void
596 {
597 assert(param.numNodes <= 256);
599 for (int i = 0; i < param.numNodes; ++i)
600 {
601 UNLKeySet.insert(UNLKeys[i]);
602 UNLNodeIDs.push_back(calcNodeID(UNLKeys[i]));
603 UNLNodeIDSet.insert(UNLNodeIDs.back());
604 }
605 }
606
611 bool
613 {
614 static uint256 fake_amemdment; // So we have different genesis ledgers
615 auto l = std::make_shared<Ledger>(
617 env.app().config(),
618 std::vector<uint256>{fake_amemdment++},
619 env.app().getNodeFamily());
621
622 // When putting validators into the negative UNL, we start with
623 // validator 0, then validator 1 ...
624 int nidx = 0;
625 while (l->seq() <= param.numLedgers)
626 {
627 l = std::make_shared<Ledger>(
628 *l, env.app().timeKeeper().closeTime());
630
631 if (l->isFlagLedger())
632 {
633 l->updateNegativeUNL();
634 OpenView accum(&*l);
635 if (l->negativeUNL().size() < param.negUNLSize)
636 {
637 auto tx = createTx(true, l->seq(), UNLKeys[nidx]);
638 if (!applyAndTestResult(env, accum, tx, true))
639 break;
640 ++nidx;
641 }
642 else if (l->negativeUNL().size() == param.negUNLSize)
643 {
645 {
646 auto tx = createTx(true, l->seq(), UNLKeys[nidx]);
647 if (!applyAndTestResult(env, accum, tx, true))
648 break;
649 ++nidx;
650 }
652 {
653 auto tx = createTx(false, l->seq(), UNLKeys[0]);
654 if (!applyAndTestResult(env, accum, tx, true))
655 break;
656 }
657 }
658 accum.apply(*l);
659 }
660 l->updateSkipList();
661 }
662 return negUnlSizeTest(
664 }
665
674 {
675 static auto keyPair = randomKeyPair(KeyType::secp256k1);
676 return std::make_shared<STValidation>(
677 env.app().timeKeeper().now(),
678 keyPair.first,
679 keyPair.second,
680 v,
681 [&](STValidation& v) {
682 v.setFieldH256(sfLedgerHash, ledger->info().hash);
683 v.setFieldU32(sfLedgerSequence, ledger->seq());
684 v.setFlag(vfFullValidation);
685 });
686 };
687
695 template <class NeedValidation>
696 void
697 walkHistoryAndAddValidations(NeedValidation&& needVal)
698 {
699 std::uint32_t curr = 0;
700 std::size_t need = 256 + 1;
701 // only last 256 + 1 ledgers need validations
702 if (history.size() > need)
703 curr = history.size() - need;
704 for (; curr != history.size(); ++curr)
705 {
706 for (std::size_t i = 0; i < param.numNodes; ++i)
707 {
708 if (needVal(history[curr], i))
709 {
711 v.setTrusted();
713 }
714 }
715 }
716 }
717
720 {
721 return history.back();
722 }
723
733};
734
735auto defaultPreVote = [](NegativeUNLVote& vote) {};
747template <typename PreVote = decltype(defaultPreVote)>
748bool
750 NetworkHistory& history,
751 NodeID const& myId,
752 std::size_t expect,
753 PreVote const& pre = defaultPreVote)
754{
755 NegativeUNLVote vote(myId, history.env.journal);
756 pre(vote);
757 auto txSet = std::make_shared<SHAMap>(
759 vote.doVoting(
760 history.lastLedger(), history.UNLKeySet, history.validations, txSet);
761 return countTx(txSet) == expect;
762}
763
768{
769 void
771 {
772 testcase("Create UNLModify Tx");
773 jtx::Env env(*this);
774
775 NodeID myId(0xA0);
776 NegativeUNLVote vote(myId, env.journal);
777
778 // one add, one remove
779 auto txSet = std::make_shared<SHAMap>(
781 PublicKey toDisableKey(
783 PublicKey toReEnableKey(
785 LedgerIndex seq(1234);
786 BEAST_EXPECT(countTx(txSet) == 0);
787 vote.addTx(seq, toDisableKey, NegativeUNLVote::ToDisable, txSet);
788 BEAST_EXPECT(countTx(txSet) == 1);
789 vote.addTx(seq, toReEnableKey, NegativeUNLVote::ToReEnable, txSet);
790 BEAST_EXPECT(countTx(txSet) == 2);
791 // content of a tx is implicitly tested after applied to a ledger
792 // in later test cases
793 }
794
795 void
797 {
798 testcase("Pick One Candidate");
799 jtx::Env env(*this);
800
801 NodeID myId(0xA0);
802 NegativeUNLVote vote(myId, env.journal);
803
804 uint256 pad_0(0);
805 uint256 pad_f = ~pad_0;
806 NodeID n_1(1);
807 NodeID n_2(2);
808 NodeID n_3(3);
809 std::vector<NodeID> candidates({n_1});
810 BEAST_EXPECT(vote.choose(pad_0, candidates) == n_1);
811 BEAST_EXPECT(vote.choose(pad_f, candidates) == n_1);
812 candidates.emplace_back(2);
813 BEAST_EXPECT(vote.choose(pad_0, candidates) == n_1);
814 BEAST_EXPECT(vote.choose(pad_f, candidates) == n_2);
815 candidates.emplace_back(3);
816 BEAST_EXPECT(vote.choose(pad_0, candidates) == n_1);
817 BEAST_EXPECT(vote.choose(pad_f, candidates) == n_3);
818 }
819
820 void
822 {
823 testcase("Build Score Table");
824 /*
825 * 1. no skip list
826 * 2. short skip list
827 * 3. local node not enough history
828 * 4. a node double validated some seq
829 * 5. local node had enough validations but on a wrong chain
830 * 6. a good case, long enough history and perfect scores
831 */
832 {
833 // 1. no skip list
834 NetworkHistory history = {*this, {10, 0, false, false, 1}};
835 BEAST_EXPECT(history.goodHistory);
836 if (history.goodHistory)
837 {
838 NegativeUNLVote vote(
839 history.UNLNodeIDs[3], history.env.journal);
840 BEAST_EXPECT(!vote.buildScoreTable(
841 history.lastLedger(),
842 history.UNLNodeIDSet,
843 history.validations));
844 }
845 }
846
847 {
848 // 2. short skip list
849 NetworkHistory history = {*this, {10, 0, false, false, 256 / 2}};
850 BEAST_EXPECT(history.goodHistory);
851 if (history.goodHistory)
852 {
853 NegativeUNLVote vote(
854 history.UNLNodeIDs[3], history.env.journal);
855 BEAST_EXPECT(!vote.buildScoreTable(
856 history.lastLedger(),
857 history.UNLNodeIDSet,
858 history.validations));
859 }
860 }
861
862 {
863 // 3. local node not enough history
864 NetworkHistory history = {*this, {10, 0, false, false, 256 + 2}};
865 BEAST_EXPECT(history.goodHistory);
866 if (history.goodHistory)
867 {
868 NodeID myId = history.UNLNodeIDs[3];
871 std::size_t idx) -> bool {
872 // skip half my validations.
873 return !(
874 history.UNLNodeIDs[idx] == myId &&
875 l->seq() % 2 == 0);
876 });
877 NegativeUNLVote vote(myId, history.env.journal);
878 BEAST_EXPECT(!vote.buildScoreTable(
879 history.lastLedger(),
880 history.UNLNodeIDSet,
881 history.validations));
882 }
883 }
884
885 {
886 // 4. a node double validated some seq
887 // 5. local node had enough validations but on a wrong chain
888 NetworkHistory history = {*this, {10, 0, false, false, 256 + 2}};
889 // We need two chains for these tests
890 bool wrongChainSuccess = history.goodHistory;
891 BEAST_EXPECT(wrongChainSuccess);
893 std::move(history.history);
894 // Create a new chain and use it as the one that majority of nodes
895 // follow
896 history.createLedgerHistory();
897 BEAST_EXPECT(history.goodHistory);
898
899 if (history.goodHistory && wrongChainSuccess)
900 {
901 NodeID myId = history.UNLNodeIDs[3];
902 NodeID badNode = history.UNLNodeIDs[4];
905 std::size_t idx) -> bool {
906 // everyone but me
907 return !(history.UNLNodeIDs[idx] == myId);
908 });
909
910 // local node validate wrong chain
911 // a node double validates
912 for (auto& l : wrongChain)
913 {
914 RCLValidation v1(history.createSTVal(l, myId));
915 history.validations.add(myId, v1);
916 RCLValidation v2(history.createSTVal(l, badNode));
917 history.validations.add(badNode, v2);
918 }
919
920 NegativeUNLVote vote(myId, history.env.journal);
921
922 // local node still on wrong chain, can build a scoreTable,
923 // but all other nodes' scores are zero
924 auto scoreTable = vote.buildScoreTable(
925 wrongChain.back(),
926 history.UNLNodeIDSet,
927 history.validations);
928 BEAST_EXPECT(scoreTable);
929 if (scoreTable)
930 {
931 for (auto const& [n, score] : *scoreTable)
932 {
933 if (n == myId)
934 BEAST_EXPECT(score == 256);
935 else
936 BEAST_EXPECT(score == 0);
937 }
938 }
939
940 // if local node switched to right history, but cannot build
941 // scoreTable because not enough local validations
942 BEAST_EXPECT(!vote.buildScoreTable(
943 history.lastLedger(),
944 history.UNLNodeIDSet,
945 history.validations));
946 }
947 }
948
949 {
950 // 6. a good case
951 NetworkHistory history = {*this, {10, 0, false, false, 256 + 1}};
952 BEAST_EXPECT(history.goodHistory);
953 if (history.goodHistory)
954 {
957 std::size_t idx) -> bool { return true; });
958 NegativeUNLVote vote(
959 history.UNLNodeIDs[3], history.env.journal);
960 auto scoreTable = vote.buildScoreTable(
961 history.lastLedger(),
962 history.UNLNodeIDSet,
963 history.validations);
964 BEAST_EXPECT(scoreTable);
965 if (scoreTable)
966 {
967 for (auto const& [_, score] : *scoreTable)
968 {
969 (void)_;
970 BEAST_EXPECT(score == 256);
971 }
972 }
973 }
974 }
975 }
976
989 bool
991 NegativeUNLVote& vote,
992 hash_set<NodeID> const& unl,
993 hash_set<NodeID> const& negUnl,
994 hash_map<NodeID, std::uint32_t> const& scoreTable,
995 std::size_t numDisable,
996 std::size_t numReEnable)
997 {
998 auto [disableCandidates, reEnableCandidates] =
999 vote.findAllCandidates(unl, negUnl, scoreTable);
1000 bool rightDisable = disableCandidates.size() == numDisable;
1001 bool rightReEnable = reEnableCandidates.size() == numReEnable;
1002 return rightDisable && rightReEnable;
1003 };
1004
1005 void
1007 {
1008 testcase("Find All Candidates");
1009 /*
1010 * -- unl size: 35
1011 * -- negUnl size: 3
1012 *
1013 * 0. all good scores
1014 * 1. all bad scores
1015 * 2. all between watermarks
1016 * 3. 2 good scorers in negUnl
1017 * 4. 2 bad scorers not in negUnl
1018 * 5. 2 in negUnl but not in unl, have a remove candidate from score
1019 * table
1020 * 6. 2 in negUnl but not in unl, no remove candidate from score table
1021 * 7. 2 new validators have good scores, already in negUnl
1022 * 8. 2 new validators have bad scores, not in negUnl
1023 * 9. expired the new validators have bad scores, not in negUnl
1024 */
1025 NetworkHistory history = {*this, {35, 0, false, false, 0}};
1026
1027 hash_set<NodeID> negUnl_012;
1028 for (std::uint32_t i = 0; i < 3; ++i)
1029 negUnl_012.insert(history.UNLNodeIDs[i]);
1030
1031 // build a good scoreTable to use, or copy and modify
1032 hash_map<NodeID, std::uint32_t> goodScoreTable;
1033 for (auto const& n : history.UNLNodeIDs)
1034 goodScoreTable[n] = NegativeUNLVote::negativeUNLHighWaterMark + 1;
1035
1036 NegativeUNLVote vote(history.UNLNodeIDs[0], history.env.journal);
1037
1038 {
1039 // all good scores
1040 BEAST_EXPECT(checkCandidateSizes(
1041 vote, history.UNLNodeIDSet, negUnl_012, goodScoreTable, 0, 3));
1042 }
1043 {
1044 // all bad scores
1046 for (auto& n : history.UNLNodeIDs)
1047 scoreTable[n] = NegativeUNLVote::negativeUNLLowWaterMark - 1;
1048 BEAST_EXPECT(checkCandidateSizes(
1049 vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 35 - 3, 0));
1050 }
1051 {
1052 // all between watermarks
1054 for (auto& n : history.UNLNodeIDs)
1055 scoreTable[n] = NegativeUNLVote::negativeUNLLowWaterMark + 1;
1056 BEAST_EXPECT(checkCandidateSizes(
1057 vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 0, 0));
1058 }
1059
1060 {
1061 // 2 good scorers in negUnl
1062 auto scoreTable = goodScoreTable;
1063 scoreTable[*negUnl_012.begin()] =
1065 BEAST_EXPECT(checkCandidateSizes(
1066 vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 0, 2));
1067 }
1068
1069 {
1070 // 2 bad scorers not in negUnl
1071 auto scoreTable = goodScoreTable;
1072 scoreTable[history.UNLNodeIDs[11]] =
1074 scoreTable[history.UNLNodeIDs[12]] =
1076 BEAST_EXPECT(checkCandidateSizes(
1077 vote, history.UNLNodeIDSet, negUnl_012, scoreTable, 2, 3));
1078 }
1079
1080 {
1081 // 2 in negUnl but not in unl, have a remove candidate from score
1082 // table
1083 hash_set<NodeID> UNL_temp = history.UNLNodeIDSet;
1084 UNL_temp.erase(history.UNLNodeIDs[0]);
1085 UNL_temp.erase(history.UNLNodeIDs[1]);
1086 BEAST_EXPECT(checkCandidateSizes(
1087 vote, UNL_temp, negUnl_012, goodScoreTable, 0, 3));
1088 }
1089
1090 {
1091 // 2 in negUnl but not in unl, no remove candidate from score table
1092 auto scoreTable = goodScoreTable;
1093 scoreTable.erase(history.UNLNodeIDs[0]);
1094 scoreTable.erase(history.UNLNodeIDs[1]);
1095 scoreTable[history.UNLNodeIDs[2]] =
1097 hash_set<NodeID> UNL_temp = history.UNLNodeIDSet;
1098 UNL_temp.erase(history.UNLNodeIDs[0]);
1099 UNL_temp.erase(history.UNLNodeIDs[1]);
1100 BEAST_EXPECT(checkCandidateSizes(
1101 vote, UNL_temp, negUnl_012, scoreTable, 0, 2));
1102 }
1103
1104 {
1105 // 2 new validators
1106 NodeID new_1(0xbead);
1107 NodeID new_2(0xbeef);
1108 hash_set<NodeID> nowTrusted = {new_1, new_2};
1109 hash_set<NodeID> UNL_temp = history.UNLNodeIDSet;
1110 UNL_temp.insert(new_1);
1111 UNL_temp.insert(new_2);
1112 vote.newValidators(256, nowTrusted);
1113 {
1114 // 2 new validators have good scores, already in negUnl
1115 auto scoreTable = goodScoreTable;
1116 scoreTable[new_1] =
1118 scoreTable[new_2] =
1120 hash_set<NodeID> negUnl_temp = negUnl_012;
1121 negUnl_temp.insert(new_1);
1122 negUnl_temp.insert(new_2);
1123 BEAST_EXPECT(checkCandidateSizes(
1124 vote, UNL_temp, negUnl_temp, scoreTable, 0, 3 + 2));
1125 }
1126 {
1127 // 2 new validators have bad scores, not in negUnl
1128 auto scoreTable = goodScoreTable;
1129 scoreTable[new_1] = 0;
1130 scoreTable[new_2] = 0;
1131 BEAST_EXPECT(checkCandidateSizes(
1132 vote, UNL_temp, negUnl_012, scoreTable, 0, 3));
1133 }
1134 {
1135 // expired the new validators have bad scores, not in negUnl
1136 vote.purgeNewValidators(
1138 auto scoreTable = goodScoreTable;
1139 scoreTable[new_1] = 0;
1140 scoreTable[new_2] = 0;
1141 BEAST_EXPECT(checkCandidateSizes(
1142 vote, UNL_temp, negUnl_012, scoreTable, 2, 3));
1143 }
1144 }
1145 }
1146
1147 void
1149 {
1150 testcase("Find All Candidates Combination");
1151 /*
1152 * == combination 1:
1153 * -- unl size: 34, 35, 80
1154 * -- nUnl size: 0, 50%, all
1155 * -- score pattern: all 0, all negativeUNLLowWaterMark & +1 & -1, all
1156 * negativeUNLHighWaterMark & +1 & -1, all 100%
1157 *
1158 * == combination 2:
1159 * -- unl size: 34, 35, 80
1160 * -- negativeUNL size: 0, all
1161 * -- nUnl size: one on, one off, one on, one off,
1162 * -- score pattern: 2*(negativeUNLLowWaterMark, +1, -1) &
1163 * 2*(negativeUNLHighWaterMark, +1, -1) & rest
1164 * negativeUNLMinLocalValsToVote
1165 */
1166
1167 jtx::Env env(*this);
1168
1169 NodeID myId(0xA0);
1170 NegativeUNLVote vote(myId, env.journal);
1171
1172 std::array<std::uint32_t, 3> unlSizes = {34, 35, 80};
1173 std::array<std::uint32_t, 3> nUnlPercent = {0, 50, 100};
1175 0,
1183
1184 //== combination 1:
1185 {
1186 auto fillScoreTable =
1187 [&](std::uint32_t unl_size,
1188 std::uint32_t nUnl_size,
1189 std::uint32_t score,
1190 hash_set<NodeID>& unl,
1191 hash_set<NodeID>& negUnl,
1192 hash_map<NodeID, std::uint32_t>& scoreTable) {
1193 std::vector<NodeID> nodeIDs;
1195 for (auto const& k : keys)
1196 {
1197 nodeIDs.emplace_back(calcNodeID(k));
1198 unl.emplace(nodeIDs.back());
1199 scoreTable[nodeIDs.back()] = score;
1200 }
1201 for (std::uint32_t i = 0; i < nUnl_size; ++i)
1202 negUnl.insert(nodeIDs[i]);
1203 };
1204
1205 for (auto us : unlSizes)
1206 {
1207 for (auto np : nUnlPercent)
1208 {
1209 for (auto score : scores)
1210 {
1211 hash_set<NodeID> unl;
1212 hash_set<NodeID> negUnl;
1214 fillScoreTable(
1215 us, us * np / 100, score, unl, negUnl, scoreTable);
1216 BEAST_EXPECT(unl.size() == us);
1217 BEAST_EXPECT(negUnl.size() == us * np / 100);
1218 BEAST_EXPECT(scoreTable.size() == us);
1219
1220 std::size_t toDisable_expect = 0;
1221 std::size_t toReEnable_expect = 0;
1222 if (np == 0)
1223 {
1224 if (score <
1226 {
1227 toDisable_expect = us;
1228 }
1229 }
1230 else if (np == 50)
1231 {
1232 if (score >
1234 {
1235 toReEnable_expect = us * np / 100;
1236 }
1237 }
1238 else
1239 {
1240 if (score >
1242 {
1243 toReEnable_expect = us;
1244 }
1245 }
1246 BEAST_EXPECT(checkCandidateSizes(
1247 vote,
1248 unl,
1249 negUnl,
1250 scoreTable,
1251 toDisable_expect,
1252 toReEnable_expect));
1253 }
1254 }
1255 }
1256
1257 //== combination 2:
1258 {
1259 auto fillScoreTable =
1260 [&](std::uint32_t unl_size,
1261 std::uint32_t nUnl_percent,
1262 hash_set<NodeID>& unl,
1263 hash_set<NodeID>& negUnl,
1264 hash_map<NodeID, std::uint32_t>& scoreTable) {
1265 std::vector<NodeID> nodeIDs;
1267 createPublicKeys(unl_size);
1268 for (auto const& k : keys)
1269 {
1270 nodeIDs.emplace_back(calcNodeID(k));
1271 unl.emplace(nodeIDs.back());
1272 }
1273
1274 std::uint32_t nIdx = 0;
1275 for (auto score : scores)
1276 {
1277 scoreTable[nodeIDs[nIdx++]] = score;
1278 scoreTable[nodeIDs[nIdx++]] = score;
1279 }
1280 for (; nIdx < unl_size;)
1281 {
1282 scoreTable[nodeIDs[nIdx++]] = scores.back();
1283 }
1284
1285 if (nUnl_percent == 100)
1286 {
1287 negUnl = unl;
1288 }
1289 else if (nUnl_percent == 50)
1290 {
1291 for (std::uint32_t i = 1; i < unl_size; i += 2)
1292 negUnl.insert(nodeIDs[i]);
1293 }
1294 };
1295
1296 for (auto us : unlSizes)
1297 {
1298 for (auto np : nUnlPercent)
1299 {
1300 hash_set<NodeID> unl;
1301 hash_set<NodeID> negUnl;
1303
1304 fillScoreTable(us, np, unl, negUnl, scoreTable);
1305 BEAST_EXPECT(unl.size() == us);
1306 BEAST_EXPECT(negUnl.size() == us * np / 100);
1307 BEAST_EXPECT(scoreTable.size() == us);
1308
1309 std::size_t toDisable_expect = 0;
1310 std::size_t toReEnable_expect = 0;
1311 if (np == 0)
1312 {
1313 toDisable_expect = 4;
1314 }
1315 else if (np == 50)
1316 {
1317 toReEnable_expect = negUnl.size() - 6;
1318 }
1319 else
1320 {
1321 toReEnable_expect = negUnl.size() - 12;
1322 }
1323 BEAST_EXPECT(checkCandidateSizes(
1324 vote,
1325 unl,
1326 negUnl,
1327 scoreTable,
1328 toDisable_expect,
1329 toReEnable_expect));
1330 }
1331 }
1332 }
1333 }
1334 }
1335
1336 void
1338 {
1339 testcase("New Validators");
1340 jtx::Env env(*this);
1341
1342 NodeID myId(0xA0);
1343 NegativeUNLVote vote(myId, env.journal);
1344
1345 // test cases:
1346 // newValidators_ of the NegativeUNLVote empty, add one
1347 // add a new one and one already added
1348 // add a new one and some already added
1349 // purge and see some are expired
1350
1351 NodeID n1(0xA1);
1352 NodeID n2(0xA2);
1353 NodeID n3(0xA3);
1354
1355 vote.newValidators(2, {n1});
1356 BEAST_EXPECT(vote.newValidators_.size() == 1);
1357 if (vote.newValidators_.size() == 1)
1358 {
1359 BEAST_EXPECT(vote.newValidators_.begin()->first == n1);
1360 BEAST_EXPECT(vote.newValidators_.begin()->second == 2);
1361 }
1362
1363 vote.newValidators(3, {n1, n2});
1364 BEAST_EXPECT(vote.newValidators_.size() == 2);
1365 if (vote.newValidators_.size() == 2)
1366 {
1367 BEAST_EXPECT(vote.newValidators_[n1] == 2);
1368 BEAST_EXPECT(vote.newValidators_[n2] == 3);
1369 }
1370
1371 vote.newValidators(
1373 BEAST_EXPECT(vote.newValidators_.size() == 3);
1374 if (vote.newValidators_.size() == 3)
1375 {
1376 BEAST_EXPECT(vote.newValidators_[n1] == 2);
1377 BEAST_EXPECT(vote.newValidators_[n2] == 3);
1378 BEAST_EXPECT(
1379 vote.newValidators_[n3] ==
1381 }
1382
1384 BEAST_EXPECT(vote.newValidators_.size() == 3);
1386 BEAST_EXPECT(vote.newValidators_.size() == 2);
1388 BEAST_EXPECT(vote.newValidators_.size() == 1);
1389 BEAST_EXPECT(vote.newValidators_.begin()->first == n3);
1390 BEAST_EXPECT(
1391 vote.newValidators_.begin()->second ==
1393 }
1394
1395 void
1396 run() override
1397 {
1398 testAddTx();
1404 }
1405};
1406
1413{
1414 void
1416 {
1417 testcase("Build Score Table Combination");
1418 /*
1419 * local node good history, correct scores:
1420 * == combination:
1421 * -- unl size: 10, 34, 35, 50
1422 * -- score pattern: all 0, all 50%, all 100%, two 0% two 50% rest 100%
1423 */
1424 std::array<std::uint32_t, 4> unlSizes = {10, 34, 35, 50};
1425 std::array<std::array<std::uint32_t, 3>, 4> scorePattern = {
1426 {{{0, 0, 0}}, {{50, 50, 50}}, {{100, 100, 100}}, {{0, 50, 100}}}};
1427
1428 for (auto unlSize : unlSizes)
1429 {
1430 for (std::uint32_t sp = 0; sp < 4; ++sp)
1431 {
1432 NetworkHistory history = {
1433 *this, {unlSize, 0, false, false, 256 + 2}};
1434 BEAST_EXPECT(history.goodHistory);
1435 if (history.goodHistory)
1436 {
1437 NodeID myId = history.UNLNodeIDs[3];
1439 [&](std::shared_ptr<Ledger const> const& l,
1440 std::size_t idx) -> bool {
1441 std::size_t k;
1442 if (idx < 2)
1443 k = 0;
1444 else if (idx < 4)
1445 k = 1;
1446 else
1447 k = 2;
1448
1449 bool add_50 =
1450 scorePattern[sp][k] == 50 && l->seq() % 2 == 0;
1451 bool add_100 = scorePattern[sp][k] == 100;
1452 bool add_me = history.UNLNodeIDs[idx] == myId;
1453 return add_50 || add_100 || add_me;
1454 });
1455
1456 NegativeUNLVote vote(myId, history.env.journal);
1457 auto scoreTable = vote.buildScoreTable(
1458 history.lastLedger(),
1459 history.UNLNodeIDSet,
1460 history.validations);
1461 BEAST_EXPECT(scoreTable);
1462 if (scoreTable)
1463 {
1464 std::uint32_t i = 0; // looping unl
1465 auto checkScores = [&](std::uint32_t score,
1466 std::uint32_t k) -> bool {
1467 if (history.UNLNodeIDs[i] == myId)
1468 return score == 256;
1469 if (scorePattern[sp][k] == 0)
1470 return score == 0;
1471 if (scorePattern[sp][k] == 50)
1472 return score == 256 / 2;
1473 if (scorePattern[sp][k] == 100)
1474 return score == 256;
1475 else
1476 return false;
1477 };
1478 for (; i < 2; ++i)
1479 {
1480 BEAST_EXPECT(checkScores(
1481 (*scoreTable)[history.UNLNodeIDs[i]], 0));
1482 }
1483 for (; i < 4; ++i)
1484 {
1485 BEAST_EXPECT(checkScores(
1486 (*scoreTable)[history.UNLNodeIDs[i]], 1));
1487 }
1488 for (; i < unlSize; ++i)
1489 {
1490 BEAST_EXPECT(checkScores(
1491 (*scoreTable)[history.UNLNodeIDs[i]], 2));
1492 }
1493 }
1494 }
1495 }
1496 }
1497 }
1498
1499 void
1500 run() override
1501 {
1503 }
1504};
1505
1506/*
1507 * Test the doVoting function of NegativeUNLVote.
1508 * The test cases are split to 5 classes for parallel execution.
1509 *
1510 * Voting tests: (use hasToDisable and hasToReEnable in some of the cases)
1511 *
1512 * == all good score, nUnl empty
1513 * -- txSet.size = 0
1514 * == all good score, nUnl not empty (use hasToDisable)
1515 * -- txSet.size = 1
1516 *
1517 * == 2 nodes offline, nUnl empty (use hasToReEnable)
1518 * -- txSet.size = 1
1519 * == 2 nodes offline, in nUnl
1520 * -- txSet.size = 0
1521 *
1522 * == 2 nodes offline, not in nUnl, but maxListed
1523 * -- txSet.size = 0
1524 *
1525 * == 2 nodes offline including me, not in nUnl
1526 * -- txSet.size = 0
1527 * == 2 nodes offline, not in negativeUNL, but I'm not a validator
1528 * -- txSet.size = 0
1529 * == 2 in nUnl, but not in unl, no other remove candidates
1530 * -- txSet.size = 1
1531 *
1532 * == 2 new validators have bad scores
1533 * -- txSet.size = 0
1534 * == 2 expired new validators have bad scores
1535 * -- txSet.size = 1
1536 */
1537
1539{
1540 void
1542 {
1543 testcase("Do Voting");
1544
1545 {
1546 //== all good score, negativeUNL empty
1547 //-- txSet.size = 0
1548 NetworkHistory history = {*this, {51, 0, false, false, {}}};
1549 BEAST_EXPECT(history.goodHistory);
1550 if (history.goodHistory)
1551 {
1553 [&](std::shared_ptr<Ledger const> const& l,
1554 std::size_t idx) -> bool { return true; });
1555 BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 0));
1556 }
1557 }
1558
1559 {
1560 // all good score, negativeUNL not empty (use hasToDisable)
1561 //-- txSet.size = 1
1562 NetworkHistory history = {*this, {37, 0, true, false, {}}};
1563 BEAST_EXPECT(history.goodHistory);
1564 if (history.goodHistory)
1565 {
1567 [&](std::shared_ptr<Ledger const> const& l,
1568 std::size_t idx) -> bool { return true; });
1569 BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 1));
1570 }
1571 }
1572 }
1573
1574 void
1575 run() override
1576 {
1577 testDoVoting();
1578 }
1579};
1580
1582{
1583 void
1585 {
1586 testcase("Do Voting");
1587
1588 {
1589 //== 2 nodes offline, negativeUNL empty (use hasToReEnable)
1590 //-- txSet.size = 1
1591 NetworkHistory history = {*this, {29, 1, false, true, {}}};
1592 BEAST_EXPECT(history.goodHistory);
1593 if (history.goodHistory)
1594 {
1596 [&](std::shared_ptr<Ledger const> const& l,
1597 std::size_t idx) -> bool {
1598 // skip node 0 and node 1
1599 return idx > 1;
1600 });
1601 BEAST_EXPECT(
1602 voteAndCheck(history, history.UNLNodeIDs.back(), 1));
1603 }
1604 }
1605
1606 {
1607 // 2 nodes offline, in negativeUNL
1608 //-- txSet.size = 0
1609 NetworkHistory history = {*this, {30, 1, true, false, {}}};
1610 BEAST_EXPECT(history.goodHistory);
1611 if (history.goodHistory)
1612 {
1613 NodeID n1 =
1614 calcNodeID(*history.lastLedger()->negativeUNL().begin());
1615 NodeID n2 =
1616 calcNodeID(*history.lastLedger()->validatorToDisable());
1618 [&](std::shared_ptr<Ledger const> const& l,
1619 std::size_t idx) -> bool {
1620 // skip node 0 and node 1
1621 return history.UNLNodeIDs[idx] != n1 &&
1622 history.UNLNodeIDs[idx] != n2;
1623 });
1624 BEAST_EXPECT(
1625 voteAndCheck(history, history.UNLNodeIDs.back(), 0));
1626 }
1627 }
1628 }
1629
1630 void
1631 run() override
1632 {
1633 testDoVoting();
1634 }
1635};
1636
1638{
1639 void
1641 {
1642 testcase("Do Voting");
1643
1644 {
1645 // 2 nodes offline, not in negativeUNL, but maxListed
1646 //-- txSet.size = 0
1647 NetworkHistory history = {*this, {32, 8, true, true, {}}};
1648 BEAST_EXPECT(history.goodHistory);
1649 if (history.goodHistory)
1650 {
1652 [&](std::shared_ptr<Ledger const> const& l,
1653 std::size_t idx) -> bool {
1654 // skip node 0 ~ 10
1655 return idx > 10;
1656 });
1657 BEAST_EXPECT(
1658 voteAndCheck(history, history.UNLNodeIDs.back(), 0));
1659 }
1660 }
1661 }
1662
1663 void
1664 run() override
1665 {
1666 testDoVoting();
1667 }
1668};
1669
1671{
1672 void
1674 {
1675 testcase("Do Voting");
1676
1677 {
1678 //== 2 nodes offline including me, not in negativeUNL
1679 //-- txSet.size = 0
1680 NetworkHistory history = {*this, {35, 0, false, false, {}}};
1681 BEAST_EXPECT(history.goodHistory);
1682 if (history.goodHistory)
1683 {
1685 [&](std::shared_ptr<Ledger const> const& l,
1686 std::size_t idx) -> bool { return idx > 1; });
1687 BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 0));
1688 }
1689 }
1690
1691 {
1692 // 2 nodes offline, not in negativeUNL, but I'm not a validator
1693 //-- txSet.size = 0
1694 NetworkHistory history = {*this, {40, 0, false, false, {}}};
1695 BEAST_EXPECT(history.goodHistory);
1696 if (history.goodHistory)
1697 {
1699 [&](std::shared_ptr<Ledger const> const& l,
1700 std::size_t idx) -> bool { return idx > 1; });
1701 BEAST_EXPECT(voteAndCheck(history, NodeID(0xdeadbeef), 0));
1702 }
1703 }
1704
1705 {
1706 //== 2 in negativeUNL, but not in unl, no other remove candidates
1707 //-- txSet.size = 1
1708 NetworkHistory history = {*this, {25, 2, false, false, {}}};
1709 BEAST_EXPECT(history.goodHistory);
1710 if (history.goodHistory)
1711 {
1713 [&](std::shared_ptr<Ledger const> const& l,
1714 std::size_t idx) -> bool { return idx > 1; });
1715 BEAST_EXPECT(voteAndCheck(
1716 history,
1717 history.UNLNodeIDs.back(),
1718 1,
1719 [&](NegativeUNLVote& vote) {
1720 history.UNLKeySet.erase(history.UNLKeys[0]);
1721 history.UNLKeySet.erase(history.UNLKeys[1]);
1722 }));
1723 }
1724 }
1725 }
1726
1727 void
1728 run() override
1729 {
1730 testDoVoting();
1731 }
1732};
1733
1735{
1736 void
1738 {
1739 testcase("Do Voting");
1740
1741 {
1742 //== 2 new validators have bad scores
1743 //-- txSet.size = 0
1744 NetworkHistory history = {*this, {15, 0, false, false, {}}};
1745 BEAST_EXPECT(history.goodHistory);
1746 if (history.goodHistory)
1747 {
1749 [&](std::shared_ptr<Ledger const> const& l,
1750 std::size_t idx) -> bool { return true; });
1751 BEAST_EXPECT(voteAndCheck(
1752 history,
1753 history.UNLNodeIDs[0],
1754 0,
1755 [&](NegativeUNLVote& vote) {
1756 auto extra_key_1 =
1757 randomKeyPair(KeyType::ed25519).first;
1758 auto extra_key_2 =
1759 randomKeyPair(KeyType::ed25519).first;
1760 history.UNLKeySet.insert(extra_key_1);
1761 history.UNLKeySet.insert(extra_key_2);
1762 hash_set<NodeID> nowTrusted;
1763 nowTrusted.insert(calcNodeID(extra_key_1));
1764 nowTrusted.insert(calcNodeID(extra_key_2));
1765 vote.newValidators(
1766 history.lastLedger()->seq(), nowTrusted);
1767 }));
1768 }
1769 }
1770
1771 {
1772 //== 2 expired new validators have bad scores
1773 //-- txSet.size = 1
1774 NetworkHistory history = {
1775 *this,
1776 {21,
1777 0,
1778 false,
1779 false,
1781 BEAST_EXPECT(history.goodHistory);
1782 if (history.goodHistory)
1783 {
1785 [&](std::shared_ptr<Ledger const> const& l,
1786 std::size_t idx) -> bool { return true; });
1787 BEAST_EXPECT(voteAndCheck(
1788 history,
1789 history.UNLNodeIDs[0],
1790 1,
1791 [&](NegativeUNLVote& vote) {
1792 auto extra_key_1 =
1793 randomKeyPair(KeyType::ed25519).first;
1794 auto extra_key_2 =
1795 randomKeyPair(KeyType::ed25519).first;
1796 history.UNLKeySet.insert(extra_key_1);
1797 history.UNLKeySet.insert(extra_key_2);
1798 hash_set<NodeID> nowTrusted;
1799 nowTrusted.insert(calcNodeID(extra_key_1));
1800 nowTrusted.insert(calcNodeID(extra_key_2));
1801 vote.newValidators(256, nowTrusted);
1802 }));
1803 }
1804 }
1805 }
1806
1807 void
1808 run() override
1809 {
1810 testDoVoting();
1811 }
1812};
1813
1815{
1816 void
1818 {
1819 testcase("Filter Validations");
1820 jtx::Env env(*this);
1821 auto l = std::make_shared<Ledger>(
1823 env.app().config(),
1825 env.app().getNodeFamily());
1826
1827 auto createSTVal = [&](std::pair<PublicKey, SecretKey> const& keys) {
1828 return std::make_shared<STValidation>(
1829 env.app().timeKeeper().now(),
1830 keys.first,
1831 keys.second,
1832 calcNodeID(keys.first),
1833 [&](STValidation& v) {
1834 v.setFieldH256(sfLedgerHash, l->info().hash);
1835 v.setFieldU32(sfLedgerSequence, l->seq());
1836 v.setFlag(vfFullValidation);
1837 });
1838 };
1839
1840 // create keys and validations
1841 std::uint32_t numNodes = 10;
1842 std::uint32_t negUnlSize = 3;
1844 hash_set<NodeID> activeValidators;
1845 hash_set<PublicKey> nUnlKeys;
1847 for (int i = 0; i < numNodes; ++i)
1848 {
1849 auto keyPair = randomKeyPair(KeyType::secp256k1);
1850 vals.emplace_back(createSTVal(keyPair));
1851 cfgKeys.push_back(toBase58(TokenType::NodePublic, keyPair.first));
1852 activeValidators.emplace(calcNodeID(keyPair.first));
1853 if (i < negUnlSize)
1854 {
1855 nUnlKeys.insert(keyPair.first);
1856 }
1857 }
1858
1859 // setup the ValidatorList
1860 auto& validators = env.app().validators();
1861 auto& local = *nUnlKeys.begin();
1862 std::vector<std::string> cfgPublishers;
1863 validators.load(local, cfgKeys, cfgPublishers);
1864 validators.updateTrusted(
1865 activeValidators,
1866 env.timeKeeper().now(),
1867 env.app().getOPs(),
1868 env.app().overlay(),
1869 env.app().getHashRouter());
1870 BEAST_EXPECT(validators.getTrustedMasterKeys().size() == numNodes);
1871 validators.setNegativeUNL(nUnlKeys);
1872 BEAST_EXPECT(validators.getNegativeUNL().size() == negUnlSize);
1873
1874 // test the filter
1875 BEAST_EXPECT(vals.size() == numNodes);
1876 vals = validators.negativeUNLFilter(std::move(vals));
1877 BEAST_EXPECT(vals.size() == numNodes - negUnlSize);
1878 }
1879
1880 void
1881 run() override
1882 {
1884 }
1885};
1886
1887BEAST_DEFINE_TESTSUITE(NegativeUNL, ledger, ripple);
1888BEAST_DEFINE_TESTSUITE(NegativeUNLNoAmendment, ledger, ripple);
1889
1890BEAST_DEFINE_TESTSUITE(NegativeUNLVoteInternal, consensus, ripple);
1891BEAST_DEFINE_TESTSUITE_MANUAL(NegativeUNLVoteScoreTable, consensus, ripple);
1892BEAST_DEFINE_TESTSUITE_PRIO(NegativeUNLVoteGoodScore, consensus, ripple, 1);
1893BEAST_DEFINE_TESTSUITE(NegativeUNLVoteOffline, consensus, ripple);
1894BEAST_DEFINE_TESTSUITE(NegativeUNLVoteMaxListed, consensus, ripple);
1895BEAST_DEFINE_TESTSUITE_PRIO(
1896 NegativeUNLVoteRetiredValidator,
1897 consensus,
1898 ripple,
1899 1);
1900BEAST_DEFINE_TESTSUITE(NegativeUNLVoteNewValidator, consensus, ripple);
1901BEAST_DEFINE_TESTSUITE(NegativeUNLVoteFilterValidations, consensus, ripple);
1902
1906bool
1909 size_t size,
1910 bool hasToDisable,
1911 bool hasToReEnable)
1912{
1913 bool sameSize = l->negativeUNL().size() == size;
1914 bool sameToDisable =
1915 (l->validatorToDisable() != std::nullopt) == hasToDisable;
1916 bool sameToReEnable =
1917 (l->validatorToReEnable() != std::nullopt) == hasToReEnable;
1918
1919 return sameSize && sameToDisable && sameToReEnable;
1920}
1921
1922bool
1923applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
1924{
1925 auto const res =
1926 apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal);
1927 if (pass)
1928 return res.ter == tesSUCCESS;
1929 else
1930 return res.ter == tefFAILURE || res.ter == temDISABLED;
1931}
1932
1933bool
1937{
1938 auto sle = l->read(keylet::negativeUNL());
1939 if (!sle)
1940 return false;
1941 if (!sle->isFieldPresent(sfDisabledValidators))
1942 return false;
1943
1944 auto const& nUnlData = sle->getFieldArray(sfDisabledValidators);
1945 if (nUnlData.size() != nUnlLedgerSeq.size())
1946 return false;
1947
1948 for (auto const& n : nUnlData)
1949 {
1950 if (!n.isFieldPresent(sfFirstLedgerSequence) ||
1951 !n.isFieldPresent(sfPublicKey))
1952 return false;
1953
1954 auto seq = n.getFieldU32(sfFirstLedgerSequence);
1955 auto d = n.getFieldVL(sfPublicKey);
1956 auto s = makeSlice(d);
1957 if (!publicKeyType(s))
1958 return false;
1959 PublicKey pk(s);
1960 auto it = nUnlLedgerSeq.find(pk);
1961 if (it == nUnlLedgerSeq.end())
1962 return false;
1963 if (it->second != seq)
1964 return false;
1965 nUnlLedgerSeq.erase(it);
1966 }
1967 return nUnlLedgerSeq.size() == 0;
1968}
1969
1972{
1973 std::size_t count = 0;
1974 for (auto i = txSet->begin(); i != txSet->end(); ++i)
1975 {
1976 ++count;
1977 }
1978 return count;
1979};
1980
1983{
1985 std::size_t ss = 33;
1986 std::vector<uint8_t> data(ss, 0);
1987 data[0] = 0xED;
1988 for (int i = 0; i < n; ++i)
1989 {
1990 data[1]++;
1991 Slice s(data.data(), ss);
1992 keys.emplace_back(s);
1993 }
1994 return keys;
1995}
1996
1997STTx
1998createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey)
1999{
2000 auto fill = [&](auto& obj) {
2001 obj.setFieldU8(sfUNLModifyDisabling, disabling ? 1 : 0);
2002 obj.setFieldU32(sfLedgerSequence, seq);
2003 obj.setFieldVL(sfUNLModifyValidator, txKey);
2004 };
2005 return STTx(ttUNL_MODIFY, fill);
2006}
2007
2008} // namespace test
2009} // namespace ripple
T back(T... args)
T begin(T... args)
A testsuite class.
Definition: suite.h:53
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:153
virtual Config & config()=0
virtual Overlay & overlay()=0
virtual TimeKeeper & timeKeeper()=0
virtual NetworkOPs & getOPs()=0
virtual ValidatorList & validators()=0
virtual Family & getNodeFamily()=0
virtual HashRouter & getHashRouter()=0
Manager to create NegativeUNL votes.
static constexpr size_t negativeUNLHighWaterMark
An unreliable validator must have more than negativeUNLHighWaterMark validations in the last flag led...
NodeID choose(uint256 const &randomPadData, std::vector< NodeID > const &candidates)
Pick one candidate from a vector of candidates.
std::optional< hash_map< NodeID, std::uint32_t > > buildScoreTable(std::shared_ptr< Ledger const > const &prevLedger, hash_set< NodeID > const &unl, RCLValidations &validations)
Build a reliability measurement score table of validators' validation messages in the last flag ledge...
void purgeNewValidators(LedgerIndex seq)
Purge validators that are not new anymore.
Candidates const findAllCandidates(hash_set< NodeID > const &unl, hash_set< NodeID > const &negUnl, hash_map< NodeID, std::uint32_t > const &scoreTable)
Process the score table and find all disabling and re-enabling candidates.
static constexpr size_t newValidatorDisableSkip
We don't want to disable new validators immediately after adding them.
static constexpr size_t negativeUNLLowWaterMark
A validator is considered unreliable if its validations is less than negativeUNLLowWaterMark in the l...
void doVoting(std::shared_ptr< Ledger const > const &prevLedger, hash_set< PublicKey > const &unlKeys, RCLValidations &validations, std::shared_ptr< SHAMap > const &initialSet)
Cast our local vote on the NegativeUNL candidates.
hash_map< NodeID, LedgerIndex > newValidators_
void newValidators(LedgerIndex seq, hash_set< NodeID > const &nowTrusted)
Notify NegativeUNLVote that new validators are added.
void addTx(LedgerIndex seq, PublicKey const &vp, NegativeUNLModify modify, std::shared_ptr< SHAMap > const &initialSet)
Add a ttUNL_MODIFY Tx to the transaction set.
static constexpr size_t negativeUNLMinLocalValsToVote
The minimum number of validations of the local node for it to participate in the voting.
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:56
void apply(TxsRawView &to) const
Apply changes.
Definition: OpenView.cpp:130
A public key.
Definition: PublicKey.h:62
Wrapper over STValidation for generic Validation code.
An immutable linear range of bytes.
Definition: Slice.h:45
time_point now() const override
Returns the current time, using the server's clock.
Definition: TimeKeeper.h:64
time_point closeTime() const
Returns the predicted close time, in network time.
Definition: TimeKeeper.h:76
ValStatus add(NodeID const &nodeID, Validation const &val)
Add a new validation.
Definition: Validations.h:623
time_point now() const override
Returns the current time.
Test the private member functions of NegativeUNLVote.
bool checkCandidateSizes(NegativeUNLVote &vote, hash_set< NodeID > const &unl, hash_set< NodeID > const &negUnl, hash_map< NodeID, std::uint32_t > const &scoreTable, std::size_t numDisable, std::size_t numReEnable)
Find all candidates and check if the number of candidates meets expectation.
Rest the build score table function of NegativeUNLVote.
void run() override
Runs the suite.
void testNegativeUNL()
Test filling and applying ttUNL_MODIFY Tx, as well as ledger update:
A transaction testing environment.
Definition: Env.h:117
Application & app()
Definition: Env.h:255
beast::Journal const journal
Definition: Env.h:158
ManualTimeKeeper & timeKeeper()
Definition: Env.h:267
T emplace_back(T... args)
T emplace(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
T insert(T... args)
Keylet const & negativeUNL() noexcept
The (fixed) index of the object containing the ledger negativeUNL.
Definition: Indexes.cpp:206
FeatureBitset supported_amendments()
Definition: Env.h:70
std::size_t countTx(std::shared_ptr< SHAMap > const &txSet)
Count the number of Tx in a TxSet.
bool negUnlSizeTest(std::shared_ptr< Ledger const > const &l, size_t size, bool hasToDisable, bool hasToReEnable)
Test the size of the negative UNL in a ledger, also test if the ledger has ToDisalbe and/or ToReEnabl...
std::vector< PublicKey > createPublicKeys(std::size_t n)
Create fake public keys.
STTx createTx(bool disabling, LedgerIndex seq, PublicKey const &txKey)
Create ttUNL_MODIFY Tx.
bool applyAndTestResult(jtx::Env &env, OpenView &view, STTx const &tx, bool pass)
Try to apply a ttUNL_MODIFY Tx, and test the apply result.
bool voteAndCheck(NetworkHistory &history, NodeID const &myId, std::size_t expect, PreVote const &pre=defaultPreVote)
Create a NegativeUNLVote object.
bool VerifyPubKeyAndSeq(std::shared_ptr< Ledger const > const &l, hash_map< PublicKey, std::uint32_t > nUnlLedgerSeq)
Verify the content of negative UNL entries (public key and ledger sequence) of a ledger.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:106
std::uint32_t LedgerIndex
A ledger index.
Definition: Protocol.h:119
base_uint< 160, detail::NodeIDTag > NodeID
NodeID is a 160-bit hash representing one node.
Definition: UintTypes.h:59
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
Definition: SecretKey.cpp:313
@ tefFAILURE
Definition: TER.h:166
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:243
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
Definition: SecretKey.cpp:281
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
Definition: PublicKey.cpp:303
@ tesSUCCESS
Definition: TER.h:242
ApplyResult apply(Application &app, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition: apply.cpp:109
create_genesis_t const create_genesis
Definition: Ledger.cpp:60
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:368
@ tapNONE
Definition: ApplyView.h:31
@ temDISABLED
Definition: TER.h:114
T push_back(T... args)
T size(T... args)
Only reasonable parameters can be honored, e.g cannot hasToReEnable when nUNLSize == 0.
std::optional< int > numLedgers
if not specified, the number of ledgers in the history is calculated from negUNLSize,...
Utility class for creating validators and ledger history.
std::shared_ptr< Ledger const > lastLedger() const
void walkHistoryAndAddValidations(NeedValidation &&needVal)
Walk the ledger history and create validation messages for the ledgers.
NetworkHistory(beast::unit_test::suite &suite, Parameter const &p)
std::vector< NodeID > UNLNodeIDs
std::shared_ptr< STValidation > createSTVal(std::shared_ptr< Ledger const > const &ledger, NodeID const &v)
Create a validation.
std::vector< PublicKey > UNLKeys
bool createLedgerHistory()
create ledger history and apply needed ttUNL_MODIFY tx at flag ledgers
hash_set< PublicKey > UNLKeySet
Set the sequence number on a JTx.
Definition: seq.h:34