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