rippled
Loading...
Searching...
No Matches
ValidatorList_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright 2015 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/misc/ValidatorList.h>
22#include <xrpld/overlay/detail/ProtocolMessage.h>
23#include <xrpl/basics/Slice.h>
24#include <xrpl/basics/base64.h>
25#include <xrpl/basics/strHex.h>
26#include <xrpl/protocol/HashPrefix.h>
27#include <xrpl/protocol/PublicKey.h>
28#include <xrpl/protocol/SecretKey.h>
29#include <xrpl/protocol/Sign.h>
30#include <xrpl/protocol/digest.h>
31#include <xrpl/protocol/jss.h>
32#include <xrpl/protocol/messages.h>
33
34#include <boost/beast/core/multi_buffer.hpp>
35
36namespace ripple {
37namespace test {
38
40{
41private:
42 struct Validator
43 {
47 };
48
49 static PublicKey
51 {
53 }
54
55 static PublicKey
57 {
59 }
60
61 static std::string
63 PublicKey const& pk,
64 SecretKey const& sk,
65 PublicKey const& spk,
66 SecretKey const& ssk,
67 int seq)
68 {
70 st[sfSequence] = seq;
71 st[sfPublicKey] = pk;
72
74 {
75 st[sfSigningPubKey] = spk;
77 }
78
79 sign(
80 st,
82 *publicKeyType(pk),
83 sk,
84 sfMasterSignature);
85
86 Serializer s;
87 st.add(s);
88
89 return std::string(static_cast<char const*>(s.data()), s.size());
90 }
91
92 static std::string
94 {
97 st[sfPublicKey] = pk;
98
99 sign(
100 st,
102 *publicKeyType(pk),
103 sk,
104 sfMasterSignature);
105
106 Serializer s;
107 st.add(s);
108
109 return std::string(static_cast<char const*>(s.data()), s.size());
110 }
111
112 static Validator
114 {
115 auto const secret = randomSecretKey();
116 auto const masterPublic = derivePublicKey(KeyType::ed25519, secret);
117 auto const signingKeys = randomKeyPair(KeyType::secp256k1);
118 return {
119 masterPublic,
120 signingKeys.first,
122 masterPublic,
123 secret,
124 signingKeys.first,
125 signingKeys.second,
126 1))};
127 }
128
131 std::vector<Validator> const& validators,
132 std::size_t sequence,
133 std::size_t validUntil,
134 std::optional<std::size_t> validFrom = {})
135 {
136 std::string data = "{\"sequence\":" + std::to_string(sequence) +
137 ",\"expiration\":" + std::to_string(validUntil);
138 if (validFrom)
139 data += ",\"effective\":" + std::to_string(*validFrom);
140 data += ",\"validators\":[";
141
142 for (auto const& val : validators)
143 {
144 data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) +
145 "\",\"manifest\":\"" + val.manifest + "\"},";
146 }
147
148 data.pop_back();
149 data += "]}";
150 return base64_encode(data);
151 }
152
155 std::string const& blob,
157 {
158 auto const data = base64_decode(blob);
159 return strHex(sign(keys.first, keys.second, makeSlice(data)));
160 }
161
162 static hash_set<NodeID>
164 {
166 res.reserve(pks.size());
167 for (auto const& pk : pks)
168 res.insert(calcNodeID(pk));
169 return res;
170 }
171
172 void
175 PublicKey pubKey,
176 ListDisposition expectedWorst,
177 ListDisposition expectedBest)
178 {
179 BEAST_EXPECT(
181 (result.publisherKey && *result.publisherKey == pubKey));
182 BEAST_EXPECT(result.bestDisposition() == expectedBest);
183 BEAST_EXPECT(result.worstDisposition() == expectedWorst);
184 }
185
186 void
188 {
189 testcase("Genesis Quorum");
190
191 ManifestCache manifests;
192 jtx::Env env(*this);
193 auto& app = env.app();
194 {
195 auto trustedKeys = std::make_unique<ValidatorList>(
196 manifests,
197 manifests,
198 env.timeKeeper(),
199 app.config().legacy("database_path"),
200 env.journal);
201 BEAST_EXPECT(trustedKeys->quorum() == 1);
202 }
203 {
204 std::size_t minQuorum = 0;
205 auto trustedKeys = std::make_unique<ValidatorList>(
206 manifests,
207 manifests,
208 env.timeKeeper(),
209 app.config().legacy("database_path"),
210 env.journal,
211 minQuorum);
212 BEAST_EXPECT(trustedKeys->quorum() == minQuorum);
213 }
214 }
215
216 void
218 {
219 testcase("Config Load");
220
221 jtx::Env env(
223 auto& app = env.app();
224 std::vector<std::string> const emptyCfgKeys;
225 std::vector<std::string> const emptyCfgPublishers;
226
227 auto const localSigningKeys = randomKeyPair(KeyType::secp256k1);
228 auto const localSigningPublicOuter = localSigningKeys.first;
229 auto const localSigningSecret = localSigningKeys.second;
230 auto const localMasterSecret = randomSecretKey();
231 auto const localMasterPublic =
232 derivePublicKey(KeyType::ed25519, localMasterSecret);
233
234 std::string const cfgManifest(makeManifestString(
235 localMasterPublic,
236 localMasterSecret,
237 localSigningPublicOuter,
238 localSigningSecret,
239 1));
240
241 auto format = [](PublicKey const& publicKey,
242 char const* comment = nullptr) {
243 auto ret = toBase58(TokenType::NodePublic, publicKey);
244
245 if (comment)
246 ret += comment;
247
248 return ret;
249 };
250
251 std::vector<PublicKey> configList;
252 configList.reserve(8);
253
254 while (configList.size() != 8)
255 configList.push_back(randomNode());
256
257 // Correct configuration
259 {format(configList[0]),
260 format(configList[1], " Comment"),
261 format(configList[2], " Multi Word Comment"),
262 format(configList[3], " Leading Whitespace"),
263 format(configList[4], " Trailing Whitespace "),
264 format(configList[5], " Leading & Trailing Whitespace "),
265 format(
266 configList[6],
267 " Leading, Trailing & Internal Whitespace "),
268 format(configList[7], " ")});
269
270 {
271 ManifestCache manifests;
272 auto trustedKeys = std::make_unique<ValidatorList>(
273 manifests,
274 manifests,
275 env.timeKeeper(),
276 app.config().legacy("database_path"),
277 env.journal);
278
279 // Correct (empty) configuration
280 BEAST_EXPECT(
281 trustedKeys->load({}, emptyCfgKeys, emptyCfgPublishers));
282
283 // load local validator key with or without manifest
284 BEAST_EXPECT(trustedKeys->load(
285 localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers));
286 BEAST_EXPECT(trustedKeys->listed(localSigningPublicOuter));
287
288 manifests.applyManifest(*deserializeManifest(cfgManifest));
289 BEAST_EXPECT(trustedKeys->load(
290 localSigningPublicOuter, emptyCfgKeys, emptyCfgPublishers));
291
292 BEAST_EXPECT(trustedKeys->listed(localMasterPublic));
293 BEAST_EXPECT(trustedKeys->listed(localSigningPublicOuter));
294 }
295 {
296 // load should add validator keys from config
297 ManifestCache manifests;
298 auto trustedKeys = std::make_unique<ValidatorList>(
299 manifests,
300 manifests,
301 env.timeKeeper(),
302 app.config().legacy("database_path"),
303 env.journal);
304
305 BEAST_EXPECT(trustedKeys->load({}, cfgKeys, emptyCfgPublishers));
306
307 for (auto const& n : configList)
308 BEAST_EXPECT(trustedKeys->listed(n));
309
310 // load should accept Ed25519 master public keys
311 auto const masterNode1 = randomMasterKey();
312 auto const masterNode2 = randomMasterKey();
313
314 std::vector<std::string> cfgMasterKeys(
315 {format(masterNode1), format(masterNode2, " Comment")});
316 BEAST_EXPECT(
317 trustedKeys->load({}, cfgMasterKeys, emptyCfgPublishers));
318 BEAST_EXPECT(trustedKeys->listed(masterNode1));
319 BEAST_EXPECT(trustedKeys->listed(masterNode2));
320
321 // load should reject invalid config keys
322 BEAST_EXPECT(
323 !trustedKeys->load({}, {"NotAPublicKey"}, emptyCfgPublishers));
324 BEAST_EXPECT(!trustedKeys->load(
325 {}, {format(randomNode(), "!")}, emptyCfgPublishers));
326
327 // load terminates when encountering an invalid entry
328 auto const goodKey = randomNode();
329 BEAST_EXPECT(!trustedKeys->load(
330 {},
331 {format(randomNode(), "!"), format(goodKey)},
332 emptyCfgPublishers));
333 BEAST_EXPECT(!trustedKeys->listed(goodKey));
334 }
335 {
336 // local validator key on config list
337 ManifestCache manifests;
338 auto trustedKeys = std::make_unique<ValidatorList>(
339 manifests,
340 manifests,
341 env.timeKeeper(),
342 app.config().legacy("database_path"),
343 env.journal);
344
345 auto const localSigningPublic =
346 parseBase58<PublicKey>(TokenType::NodePublic, cfgKeys.front());
347
348 BEAST_EXPECT(trustedKeys->load(
349 *localSigningPublic, cfgKeys, emptyCfgPublishers));
350
351 BEAST_EXPECT(trustedKeys->localPublicKey() == localSigningPublic);
352 BEAST_EXPECT(trustedKeys->listed(*localSigningPublic));
353 for (auto const& n : configList)
354 BEAST_EXPECT(trustedKeys->listed(n));
355 }
356 {
357 // local validator key not on config list
358 ManifestCache manifests;
359 auto trustedKeys = std::make_unique<ValidatorList>(
360 manifests,
361 manifests,
362 env.timeKeeper(),
363 app.config().legacy("database_path"),
364 env.journal);
365
366 auto const localSigningPublic = randomNode();
367 BEAST_EXPECT(trustedKeys->load(
368 localSigningPublic, cfgKeys, emptyCfgPublishers));
369
370 BEAST_EXPECT(trustedKeys->localPublicKey() == localSigningPublic);
371 BEAST_EXPECT(trustedKeys->listed(localSigningPublic));
372 for (auto const& n : configList)
373 BEAST_EXPECT(trustedKeys->listed(n));
374 }
375 {
376 // local validator key (with manifest) not on config list
377 ManifestCache manifests;
378 auto trustedKeys = std::make_unique<ValidatorList>(
379 manifests,
380 manifests,
381 env.timeKeeper(),
382 app.config().legacy("database_path"),
383 env.journal);
384
385 manifests.applyManifest(*deserializeManifest(cfgManifest));
386
387 BEAST_EXPECT(trustedKeys->load(
388 localSigningPublicOuter, cfgKeys, emptyCfgPublishers));
389
390 BEAST_EXPECT(trustedKeys->localPublicKey() == localMasterPublic);
391 BEAST_EXPECT(trustedKeys->listed(localSigningPublicOuter));
392 BEAST_EXPECT(trustedKeys->listed(localMasterPublic));
393 for (auto const& n : configList)
394 BEAST_EXPECT(trustedKeys->listed(n));
395 }
396 {
397 ManifestCache manifests;
398 auto trustedKeys = std::make_unique<ValidatorList>(
399 manifests,
400 manifests,
401 env.timeKeeper(),
402 app.config().legacy("database_path"),
403 env.journal);
404
405 // load should reject invalid validator list signing keys
406 std::vector<std::string> badPublishers({"NotASigningKey"});
407 BEAST_EXPECT(!trustedKeys->load({}, emptyCfgKeys, badPublishers));
408
409 // load should reject validator list signing keys with invalid
410 // encoding
413 badPublishers.clear();
414 for (auto const& key : keys)
415 badPublishers.push_back(toBase58(TokenType::NodePublic, key));
416
417 BEAST_EXPECT(!trustedKeys->load({}, emptyCfgKeys, badPublishers));
418 for (auto const& key : keys)
419 BEAST_EXPECT(!trustedKeys->trustedPublisher(key));
420
421 // load should accept valid validator list publisher keys
422 std::vector<std::string> cfgPublishers;
423 for (auto const& key : keys)
424 cfgPublishers.push_back(strHex(key));
425
426 BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers));
427 for (auto const& key : keys)
428 BEAST_EXPECT(trustedKeys->trustedPublisher(key));
429 }
430 {
431 // Attempt to load a publisher key that has been revoked.
432 // Should fail
433 ManifestCache valManifests;
434 ManifestCache pubManifests;
435 auto trustedKeys = std::make_unique<ValidatorList>(
436 valManifests,
437 pubManifests,
438 env.timeKeeper(),
439 app.config().legacy("database_path"),
440 env.journal);
441
442 auto const pubRevokedSecret = randomSecretKey();
443 auto const pubRevokedPublic =
444 derivePublicKey(KeyType::ed25519, pubRevokedSecret);
445 auto const pubRevokedSigning = randomKeyPair(KeyType::secp256k1);
446 // make this manifest revoked (seq num = max)
447 // -- thus should not be loaded
449 pubRevokedPublic,
450 pubRevokedSecret,
451 pubRevokedSigning.first,
452 pubRevokedSigning.second,
454
455 // this one is not revoked (and not in manifest cache at all.)
456 auto legitKey = randomMasterKey();
457
458 std::vector<std::string> cfgPublishers = {
459 strHex(pubRevokedPublic), strHex(legitKey)};
460 BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers));
461
462 BEAST_EXPECT(!trustedKeys->trustedPublisher(pubRevokedPublic));
463 BEAST_EXPECT(trustedKeys->trustedPublisher(legitKey));
464 }
465 }
466
467 void
469 {
470 testcase("Apply list");
471 using namespace std::chrono_literals;
472
473 std::string const siteUri = "testApplyList.test";
474
475 auto checkAvailable =
476 [this](
477 auto const& trustedKeys,
478 auto const& hexPublic,
479 auto const& manifest,
480 auto const version,
482 expected) {
483 const auto available = trustedKeys->getAvailable(hexPublic);
484
485 BEAST_EXPECT(!version || available);
486 if (available)
487 {
488 auto const& a = *available;
489 BEAST_EXPECT(a[jss::public_key] == hexPublic);
490 BEAST_EXPECT(a[jss::manifest] == manifest);
491 // Because multiple lists were processed, the version was
492 // overridden
493 BEAST_EXPECT(a[jss::version] == version);
494 if (version == 1)
495 {
496 BEAST_EXPECT(expected.size() == 1);
497 BEAST_EXPECT(a[jss::blob] == expected[0].first);
498 BEAST_EXPECT(a[jss::signature] == expected[0].second);
499 BEAST_EXPECT(!a.isMember(jss::blobs_v2));
500 }
501 else if (BEAST_EXPECT(a.isMember(jss::blobs_v2)))
502 {
503 BEAST_EXPECT(!a.isMember(jss::blob));
504 BEAST_EXPECT(!a.isMember(jss::signature));
505 auto const& blobs_v2 = a[jss::blobs_v2];
506 BEAST_EXPECT(
507 blobs_v2.isArray() &&
508 blobs_v2.size() == expected.size());
509
510 for (unsigned int i = 0; i < expected.size(); ++i)
511 {
512 BEAST_EXPECT(
513 blobs_v2[i][jss::blob] == expected[i].first);
514 BEAST_EXPECT(
515 blobs_v2[i][jss::signature] ==
516 expected[i].second);
517 }
518 }
519 }
520 };
521
522 ManifestCache manifests;
523 jtx::Env env(*this);
524 auto& app = env.app();
525 auto trustedKeys = std::make_unique<ValidatorList>(
526 manifests,
527 manifests,
528 env.app().timeKeeper(),
529 app.config().legacy("database_path"),
530 env.journal);
531
532 auto expectTrusted =
533 [this, &trustedKeys](std::vector<Validator> const& list) {
534 for (auto const& val : list)
535 {
536 BEAST_EXPECT(trustedKeys->listed(val.masterPublic));
537 BEAST_EXPECT(trustedKeys->listed(val.signingPublic));
538 }
539 };
540
541 auto expectUntrusted =
542 [this, &trustedKeys](std::vector<Validator> const& list) {
543 for (auto const& val : list)
544 {
545 BEAST_EXPECT(!trustedKeys->listed(val.masterPublic));
546 BEAST_EXPECT(!trustedKeys->listed(val.signingPublic));
547 }
548 };
549
550 auto const publisherSecret = randomSecretKey();
551 auto const publisherPublic =
552 derivePublicKey(KeyType::ed25519, publisherSecret);
553 const auto hexPublic =
554 strHex(publisherPublic.begin(), publisherPublic.end());
555 auto const pubSigningKeys1 = randomKeyPair(KeyType::secp256k1);
556 auto const manifest1 = base64_encode(makeManifestString(
557 publisherPublic,
558 publisherSecret,
559 pubSigningKeys1.first,
560 pubSigningKeys1.second,
561 1));
562
563 std::vector<std::string> cfgKeys1({strHex(publisherPublic)});
564 std::vector<std::string> emptyCfgKeys;
565
566 BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgKeys1));
567
569 auto constexpr listSize = 20;
570 auto constexpr numLists = 9;
572 // 1-based to correspond with the individually named blobs below.
573 for (auto i = 1; i <= numLists; ++i)
574 {
575 auto& list = lists[i];
576 list.reserve(listSize);
577 while (list.size() < listSize)
578 list.push_back(randomValidator());
579 }
580 return lists;
581 }();
582
583 // Attempt an expired list (fail) and a single list (succeed)
584 env.timeKeeper().set(env.timeKeeper().now() + 1s);
585 auto const version = 1;
586 auto const sequence1 = 1;
587 auto const expiredblob = makeList(
588 lists.at(1),
589 sequence1,
590 env.timeKeeper().now().time_since_epoch().count());
591 auto const expiredSig = signList(expiredblob, pubSigningKeys1);
592
593 NetClock::time_point const validUntil = env.timeKeeper().now() + 3600s;
594 auto const sequence2 = 2;
595 auto const blob2 = makeList(
596 lists.at(2), sequence2, validUntil.time_since_epoch().count());
597 auto const sig2 = signList(blob2, pubSigningKeys1);
598
600 trustedKeys->applyLists(
601 manifest1,
602 version,
603 {{expiredblob, expiredSig, {}}, {blob2, sig2, {}}},
604 siteUri),
605 publisherPublic,
608
609 expectTrusted(lists.at(2));
610
611 checkAvailable(
612 trustedKeys, hexPublic, manifest1, version, {{blob2, sig2}});
613
614 // Do not apply future lists, but process them
615 auto const version2 = 2;
616 auto const sequence7 = 7;
617 auto const effective7 = validUntil - 60s;
618 auto const expiration7 = effective7 + 3600s;
619 auto const blob7 = makeList(
620 lists.at(7),
621 sequence7,
622 expiration7.time_since_epoch().count(),
623 effective7.time_since_epoch().count());
624 auto const sig7 = signList(blob7, pubSigningKeys1);
625
626 auto const sequence8 = 8;
627 auto const effective8 = expiration7 - 60s;
628 auto const expiration8 = effective8 + 3600s;
629 auto const blob8 = makeList(
630 lists.at(8),
631 sequence8,
632 expiration8.time_since_epoch().count(),
633 effective8.time_since_epoch().count());
634 auto const sig8 = signList(blob8, pubSigningKeys1);
635
636 checkResult(
637 trustedKeys->applyLists(
638 manifest1,
639 version2,
640 {{blob7, sig7, {}}, {blob8, sig8, {}}},
641 siteUri),
642 publisherPublic,
643 ListDisposition::pending,
644 ListDisposition::pending);
645
646 expectUntrusted(lists.at(7));
647 expectUntrusted(lists.at(8));
648
649 // Do not apply out-of-order future list, but process it
650 auto const sequence6 = 6;
651 auto const effective6 = effective7 - 60s;
652 auto const expiration6 = effective6 + 3600s;
653 auto const blob6 = makeList(
654 lists.at(6),
655 sequence6,
656 expiration6.time_since_epoch().count(),
657 effective6.time_since_epoch().count());
658 auto const sig6 = signList(blob6, pubSigningKeys1);
659
660 // Process future list that is overridden by a later list
661 auto const sequence6a = 5;
662 auto const effective6a = effective6 + 60s;
663 auto const expiration6a = effective6a + 3600s;
664 auto const blob6a = makeList(
665 lists.at(5),
666 sequence6a,
667 expiration6a.time_since_epoch().count(),
668 effective6a.time_since_epoch().count());
669 auto const sig6a = signList(blob6a, pubSigningKeys1);
670
671 checkResult(
672 trustedKeys->applyLists(
673 manifest1,
674 version,
675 {{blob6a, sig6a, {}}, {blob6, sig6, {}}},
676 siteUri),
677 publisherPublic,
678 ListDisposition::pending,
679 ListDisposition::pending);
680
681 expectUntrusted(lists.at(6));
682 expectTrusted(lists.at(2));
683
684 // Do not apply re-process lists known future sequence numbers
685
686 checkResult(
687 trustedKeys->applyLists(
688 manifest1,
689 version,
690 {{blob7, sig7, {}}, {blob6, sig6, {}}},
691 siteUri),
692 publisherPublic,
693 ListDisposition::known_sequence,
694 ListDisposition::known_sequence);
695
696 expectUntrusted(lists.at(6));
697 expectUntrusted(lists.at(7));
698 expectTrusted(lists.at(2));
699
700 // do not use list from untrusted publisher
701 auto const untrustedManifest = base64_encode(makeManifestString(
702 randomMasterKey(),
703 publisherSecret,
704 pubSigningKeys1.first,
705 pubSigningKeys1.second,
706 1));
707
708 checkResult(
709 trustedKeys->applyLists(
710 untrustedManifest, version, {{blob2, sig2, {}}}, siteUri),
711 publisherPublic,
712 ListDisposition::untrusted,
713 ListDisposition::untrusted);
714
715 // do not use list with unhandled version
716 auto const badVersion = 666;
717 checkResult(
718 trustedKeys->applyLists(
719 manifest1, badVersion, {{blob2, sig2, {}}}, siteUri),
720 publisherPublic,
721 ListDisposition::unsupported_version,
722 ListDisposition::unsupported_version);
723
724 // apply list with highest sequence number
725 auto const sequence3 = 3;
726 auto const blob3 = makeList(
727 lists.at(3), sequence3, validUntil.time_since_epoch().count());
728 auto const sig3 = signList(blob3, pubSigningKeys1);
729
730 checkResult(
731 trustedKeys->applyLists(
732 manifest1, version, {{blob3, sig3, {}}}, siteUri),
733 publisherPublic,
734 ListDisposition::accepted,
735 ListDisposition::accepted);
736
737 expectUntrusted(lists.at(1));
738 expectUntrusted(lists.at(2));
739 expectTrusted(lists.at(3));
740
741 // Note that blob6a is not present, because it was dropped during
742 // processing
743 checkAvailable(
744 trustedKeys,
745 hexPublic,
746 manifest1,
747 2,
748 {{blob3, sig3}, {blob6, sig6}, {blob7, sig7}, {blob8, sig8}});
749
750 // do not re-apply lists with past or current sequence numbers
751 checkResult(
752 trustedKeys->applyLists(
753 manifest1,
754 version,
755 {{blob2, sig2, {}}, {blob3, sig3, {}}},
756 siteUri),
757 publisherPublic,
758 ListDisposition::stale,
759 ListDisposition::same_sequence);
760
761 // apply list with new publisher key updated by manifest. Also send some
762 // old lists along with the old manifest
763 auto const pubSigningKeys2 = randomKeyPair(KeyType::secp256k1);
764 auto manifest2 = base64_encode(makeManifestString(
765 publisherPublic,
766 publisherSecret,
767 pubSigningKeys2.first,
768 pubSigningKeys2.second,
769 2));
770
771 auto const sequence4 = 4;
772 auto const blob4 = makeList(
773 lists.at(4), sequence4, validUntil.time_since_epoch().count());
774 auto const sig4 = signList(blob4, pubSigningKeys2);
775
776 checkResult(
777 trustedKeys->applyLists(
778 manifest2,
779 version,
780 {{blob2, sig2, manifest1},
781 {blob3, sig3, manifest1},
782 {blob4, sig4, {}}},
783 siteUri),
784 publisherPublic,
785 ListDisposition::stale,
786 ListDisposition::accepted);
787
788 expectUntrusted(lists.at(2));
789 expectUntrusted(lists.at(3));
790 expectTrusted(lists.at(4));
791
792 checkAvailable(
793 trustedKeys,
794 hexPublic,
795 manifest2,
796 2,
797 {{blob4, sig4}, {blob6, sig6}, {blob7, sig7}, {blob8, sig8}});
798
799 auto const sequence5 = 5;
800 auto const blob5 = makeList(
801 lists.at(5), sequence5, validUntil.time_since_epoch().count());
802 auto const badSig = signList(blob5, pubSigningKeys1);
803 checkResult(
804 trustedKeys->applyLists(
805 manifest1, version, {{blob5, badSig, {}}}, siteUri),
806 publisherPublic,
807 ListDisposition::invalid,
808 ListDisposition::invalid);
809
810 expectUntrusted(lists.at(2));
811 expectUntrusted(lists.at(3));
812 expectTrusted(lists.at(4));
813 expectUntrusted(lists.at(5));
814
815 // Reprocess the pending list, but the signature is no longer valid
816 checkResult(
817 trustedKeys->applyLists(
818 manifest1,
819 version,
820 {{blob7, sig7, {}}, {blob8, sig8, {}}},
821 siteUri),
822 publisherPublic,
823 ListDisposition::invalid,
824 ListDisposition::invalid);
825
826 expectTrusted(lists.at(4));
827 expectUntrusted(lists.at(7));
828 expectUntrusted(lists.at(8));
829
830 // Automatically rotate the first pending already processed list using
831 // updateTrusted. Note that the timekeeper is NOT moved, so the close
832 // time will be ahead of the test's wall clock
833 trustedKeys->updateTrusted(
834 {},
835 effective6 + 1s,
836 env.app().getOPs(),
837 env.app().overlay(),
838 env.app().getHashRouter());
839
840 expectUntrusted(lists.at(3));
841 expectTrusted(lists.at(6));
842
843 checkAvailable(
844 trustedKeys,
845 hexPublic,
846 manifest2,
847 2,
848 {{blob6, sig6}, {blob7, sig7}, {blob8, sig8}});
849
850 // Automatically rotate the LAST pending list using updateTrusted,
851 // bypassing blob7. Note that the timekeeper IS moved, so the provided
852 // close time will be behind the test's wall clock, and thus the wall
853 // clock is used.
854 env.timeKeeper().set(effective8);
855 trustedKeys->updateTrusted(
856 {},
857 effective8 + 1s,
858 env.app().getOPs(),
859 env.app().overlay(),
860 env.app().getHashRouter());
861
862 expectUntrusted(lists.at(6));
863 expectUntrusted(lists.at(7));
864 expectTrusted(lists.at(8));
865
866 checkAvailable(trustedKeys, hexPublic, manifest2, 2, {{blob8, sig8}});
867
868 // resign the pending list with new key and validate it, but it's
869 // already valid Also try reprocessing the pending list with an
870 // explicit manifest
871 // - it is still invalid
872 auto const sig8_2 = signList(blob8, pubSigningKeys2);
873
874 checkResult(
875 trustedKeys->applyLists(
876 manifest2,
877 version,
878 {{blob8, sig8, manifest1}, {blob8, sig8_2, {}}},
879 siteUri),
880 publisherPublic,
881 ListDisposition::invalid,
882 ListDisposition::same_sequence);
883
884 expectTrusted(lists.at(8));
885
886 checkAvailable(trustedKeys, hexPublic, manifest2, 2, {{blob8, sig8}});
887
888 // do not apply list with revoked publisher key
889 // applied list is removed due to revoked publisher key
890 auto const signingKeysMax = randomKeyPair(KeyType::secp256k1);
891 auto maxManifest = base64_encode(
892 makeRevocationString(publisherPublic, publisherSecret));
893
894 auto const sequence9 = 9;
895 auto const blob9 = makeList(
896 lists.at(9), sequence9, validUntil.time_since_epoch().count());
897 auto const sig9 = signList(blob9, signingKeysMax);
898
899 checkResult(
900 trustedKeys->applyLists(
901 maxManifest, version, {{blob9, sig9, {}}}, siteUri),
902 publisherPublic,
903 ListDisposition::untrusted,
904 ListDisposition::untrusted);
905
906 BEAST_EXPECT(!trustedKeys->trustedPublisher(publisherPublic));
907 for (auto const& [num, list] : lists)
908 {
909 (void)num;
910 expectUntrusted(list);
911 }
912
913 checkAvailable(trustedKeys, hexPublic, manifest2, 0, {});
914 }
915
916 void
918 {
919 testcase("GetAvailable");
920 using namespace std::chrono_literals;
921
922 std::string const siteUri = "testApplyList.test";
923
924 ManifestCache manifests;
925 jtx::Env env(*this);
926 auto& app = env.app();
927 auto trustedKeys = std::make_unique<ValidatorList>(
928 manifests,
929 manifests,
930 env.app().timeKeeper(),
931 app.config().legacy("database_path"),
932 env.journal);
933
934 auto const publisherSecret = randomSecretKey();
935 auto const publisherPublic =
936 derivePublicKey(KeyType::ed25519, publisherSecret);
937 const auto hexPublic =
938 strHex(publisherPublic.begin(), publisherPublic.end());
939 auto const pubSigningKeys1 = randomKeyPair(KeyType::secp256k1);
940 auto const manifest = base64_encode(makeManifestString(
941 publisherPublic,
942 publisherSecret,
943 pubSigningKeys1.first,
944 pubSigningKeys1.second,
945 1));
946
947 std::vector<std::string> cfgKeys1({strHex(publisherPublic)});
948 std::vector<std::string> emptyCfgKeys;
949
950 BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgKeys1));
951
952 std::vector<Validator> const list = []() {
953 auto constexpr listSize = 20;
955 list.reserve(listSize);
956 while (list.size() < listSize)
957 list.push_back(randomValidator());
958 return list;
959 }();
960
961 // Process a list
962 env.timeKeeper().set(env.timeKeeper().now() + 1s);
963 NetClock::time_point const validUntil = env.timeKeeper().now() + 3600s;
964 auto const blob =
965 makeList(list, 1, validUntil.time_since_epoch().count());
966 auto const sig = signList(blob, pubSigningKeys1);
967
968 {
969 // list unavailable
970 auto const available = trustedKeys->getAvailable(hexPublic);
971 BEAST_EXPECT(!available);
972 }
973
974 BEAST_EXPECT(
975 trustedKeys->applyLists(manifest, 1, {{blob, sig, {}}}, siteUri)
976 .bestDisposition() == ListDisposition::accepted);
977
978 {
979 // invalid public key
980 auto const available =
981 trustedKeys->getAvailable(hexPublic + "invalid", 1);
982 BEAST_EXPECT(!available);
983 }
984
985 {
986 // unknown public key
987 auto const badSecret = randomSecretKey();
988 auto const badPublic = derivePublicKey(KeyType::ed25519, badSecret);
989 const auto hexBad = strHex(badPublic.begin(), badPublic.end());
990
991 auto const available = trustedKeys->getAvailable(hexBad, 1);
992 BEAST_EXPECT(!available);
993 }
994 {
995 // bad version 0
996 auto const available = trustedKeys->getAvailable(hexPublic, 0);
997 if (BEAST_EXPECT(available))
998 {
999 auto const& a = *available;
1000 BEAST_EXPECT(!a);
1001 }
1002 }
1003 {
1004 // bad version 3
1005 auto const available = trustedKeys->getAvailable(hexPublic, 3);
1006 if (BEAST_EXPECT(available))
1007 {
1008 auto const& a = *available;
1009 BEAST_EXPECT(!a);
1010 }
1011 }
1012 {
1013 // version 1
1014 auto const available = trustedKeys->getAvailable(hexPublic, 1);
1015 if (BEAST_EXPECT(available))
1016 {
1017 auto const& a = *available;
1018 BEAST_EXPECT(a[jss::public_key] == hexPublic);
1019 BEAST_EXPECT(a[jss::manifest] == manifest);
1020 BEAST_EXPECT(a[jss::version] == 1);
1021
1022 BEAST_EXPECT(a[jss::blob] == blob);
1023 BEAST_EXPECT(a[jss::signature] == sig);
1024 BEAST_EXPECT(!a.isMember(jss::blobs_v2));
1025 }
1026 }
1027
1028 {
1029 // version 2
1030 auto const available = trustedKeys->getAvailable(hexPublic, 2);
1031 if (BEAST_EXPECT(available))
1032 {
1033 auto const& a = *available;
1034 BEAST_EXPECT(a[jss::public_key] == hexPublic);
1035 BEAST_EXPECT(a[jss::manifest] == manifest);
1036 BEAST_EXPECT(a[jss::version] == 2);
1037
1038 if (BEAST_EXPECT(a.isMember(jss::blobs_v2)))
1039 {
1040 BEAST_EXPECT(!a.isMember(jss::blob));
1041 BEAST_EXPECT(!a.isMember(jss::signature));
1042 auto const& blobs_v2 = a[jss::blobs_v2];
1043 BEAST_EXPECT(blobs_v2.isArray() && blobs_v2.size() == 1);
1044
1045 BEAST_EXPECT(blobs_v2[0u][jss::blob] == blob);
1046 BEAST_EXPECT(blobs_v2[0u][jss::signature] == sig);
1047 }
1048 }
1049 }
1050 }
1051
1052 void
1054 {
1055 testcase("Update trusted");
1056
1057 std::string const siteUri = "testUpdateTrusted.test";
1058
1059 ManifestCache manifestsOuter;
1060 jtx::Env env(*this);
1061 auto& app = env.app();
1062 auto trustedKeysOuter = std::make_unique<ValidatorList>(
1063 manifestsOuter,
1064 manifestsOuter,
1065 env.timeKeeper(),
1066 app.config().legacy("database_path"),
1067 env.journal);
1068
1069 std::vector<std::string> cfgPublishersOuter;
1070 hash_set<NodeID> activeValidatorsOuter;
1071
1072 std::size_t const maxKeys = 40;
1073 {
1075 cfgKeys.reserve(maxKeys);
1076 hash_set<NodeID> unseenValidators;
1077
1078 while (cfgKeys.size() != maxKeys)
1079 {
1080 auto const valKey = randomNode();
1081 cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey));
1082 if (cfgKeys.size() <= maxKeys - 5)
1083 activeValidatorsOuter.emplace(calcNodeID(valKey));
1084 else
1085 unseenValidators.emplace(calcNodeID(valKey));
1086 }
1087
1088 BEAST_EXPECT(
1089 trustedKeysOuter->load({}, cfgKeys, cfgPublishersOuter));
1090
1091 // updateTrusted should make all configured validators trusted
1092 // even if they are not active/seen
1093 TrustChanges changes = trustedKeysOuter->updateTrusted(
1094 activeValidatorsOuter,
1095 env.timeKeeper().now(),
1096 env.app().getOPs(),
1097 env.app().overlay(),
1098 env.app().getHashRouter());
1099
1100 for (auto const& val : unseenValidators)
1101 activeValidatorsOuter.emplace(val);
1102
1103 BEAST_EXPECT(changes.added == activeValidatorsOuter);
1104 BEAST_EXPECT(changes.removed.empty());
1105 BEAST_EXPECT(
1106 trustedKeysOuter->quorum() == std::ceil(cfgKeys.size() * 0.8f));
1107 for (auto const& val : cfgKeys)
1108 {
1109 if (auto const valKey =
1110 parseBase58<PublicKey>(TokenType::NodePublic, val))
1111 {
1112 BEAST_EXPECT(trustedKeysOuter->listed(*valKey));
1113 BEAST_EXPECT(trustedKeysOuter->trusted(*valKey));
1114 }
1115 else
1116 fail();
1117 }
1118
1119 changes = trustedKeysOuter->updateTrusted(
1120 activeValidatorsOuter,
1121 env.timeKeeper().now(),
1122 env.app().getOPs(),
1123 env.app().overlay(),
1124 env.app().getHashRouter());
1125 BEAST_EXPECT(changes.added.empty());
1126 BEAST_EXPECT(changes.removed.empty());
1127 BEAST_EXPECT(
1128 trustedKeysOuter->quorum() == std::ceil(cfgKeys.size() * 0.8f));
1129 }
1130 {
1131 // update with manifests
1132 auto const masterPrivate = randomSecretKey();
1133 auto const masterPublic =
1134 derivePublicKey(KeyType::ed25519, masterPrivate);
1135
1137 {toBase58(TokenType::NodePublic, masterPublic)});
1138
1139 BEAST_EXPECT(
1140 trustedKeysOuter->load({}, cfgKeys, cfgPublishersOuter));
1141
1142 auto const signingKeys1 = randomKeyPair(KeyType::secp256k1);
1143 auto const signingPublic1 = signingKeys1.first;
1144 activeValidatorsOuter.emplace(calcNodeID(masterPublic));
1145
1146 // Should not trust ephemeral signing key if there is no manifest
1147 TrustChanges changes = trustedKeysOuter->updateTrusted(
1148 activeValidatorsOuter,
1149 env.timeKeeper().now(),
1150 env.app().getOPs(),
1151 env.app().overlay(),
1152 env.app().getHashRouter());
1153 BEAST_EXPECT(changes.added == asNodeIDs({masterPublic}));
1154 BEAST_EXPECT(changes.removed.empty());
1155 BEAST_EXPECT(
1156 trustedKeysOuter->quorum() == std::ceil((maxKeys + 1) * 0.8f));
1157 BEAST_EXPECT(trustedKeysOuter->listed(masterPublic));
1158 BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic));
1159 BEAST_EXPECT(!trustedKeysOuter->listed(signingPublic1));
1160 BEAST_EXPECT(!trustedKeysOuter->trusted(signingPublic1));
1161
1162 // Should trust the ephemeral signing key from the applied manifest
1163 auto m1 = deserializeManifest(makeManifestString(
1164 masterPublic,
1165 masterPrivate,
1166 signingPublic1,
1167 signingKeys1.second,
1168 1));
1169
1170 BEAST_EXPECT(
1171 manifestsOuter.applyManifest(std::move(*m1)) ==
1172 ManifestDisposition::accepted);
1173 BEAST_EXPECT(trustedKeysOuter->listed(masterPublic));
1174 BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic));
1175 BEAST_EXPECT(trustedKeysOuter->listed(signingPublic1));
1176 BEAST_EXPECT(trustedKeysOuter->trusted(signingPublic1));
1177
1178 // Should only trust the ephemeral signing key
1179 // from the newest applied manifest
1180 auto const signingKeys2 = randomKeyPair(KeyType::secp256k1);
1181 auto const signingPublic2 = signingKeys2.first;
1182 auto m2 = deserializeManifest(makeManifestString(
1183 masterPublic,
1184 masterPrivate,
1185 signingPublic2,
1186 signingKeys2.second,
1187 2));
1188 BEAST_EXPECT(
1189 manifestsOuter.applyManifest(std::move(*m2)) ==
1190 ManifestDisposition::accepted);
1191 BEAST_EXPECT(trustedKeysOuter->listed(masterPublic));
1192 BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic));
1193 BEAST_EXPECT(trustedKeysOuter->listed(signingPublic2));
1194 BEAST_EXPECT(trustedKeysOuter->trusted(signingPublic2));
1195 BEAST_EXPECT(!trustedKeysOuter->listed(signingPublic1));
1196 BEAST_EXPECT(!trustedKeysOuter->trusted(signingPublic1));
1197
1198 // Should not trust keys from revoked master public key
1199 auto const signingKeysMax = randomKeyPair(KeyType::secp256k1);
1200 auto const signingPublicMax = signingKeysMax.first;
1201 activeValidatorsOuter.emplace(calcNodeID(signingPublicMax));
1202 auto mMax = deserializeManifest(
1203 makeRevocationString(masterPublic, masterPrivate));
1204
1205 BEAST_EXPECT(mMax->revoked());
1206 BEAST_EXPECT(
1207 manifestsOuter.applyManifest(std::move(*mMax)) ==
1208 ManifestDisposition::accepted);
1209 BEAST_EXPECT(
1210 manifestsOuter.getSigningKey(masterPublic) == masterPublic);
1211 BEAST_EXPECT(manifestsOuter.revoked(masterPublic));
1212
1213 // Revoked key remains trusted until list is updated
1214 BEAST_EXPECT(trustedKeysOuter->listed(masterPublic));
1215 BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic));
1216
1217 changes = trustedKeysOuter->updateTrusted(
1218 activeValidatorsOuter,
1219 env.timeKeeper().now(),
1220 env.app().getOPs(),
1221 env.app().overlay(),
1222 env.app().getHashRouter());
1223 BEAST_EXPECT(changes.removed == asNodeIDs({masterPublic}));
1224 BEAST_EXPECT(changes.added.empty());
1225 BEAST_EXPECT(
1226 trustedKeysOuter->quorum() == std::ceil(maxKeys * 0.8f));
1227 BEAST_EXPECT(trustedKeysOuter->listed(masterPublic));
1228 BEAST_EXPECT(!trustedKeysOuter->trusted(masterPublic));
1229 BEAST_EXPECT(!trustedKeysOuter->listed(signingPublicMax));
1230 BEAST_EXPECT(!trustedKeysOuter->trusted(signingPublicMax));
1231 BEAST_EXPECT(!trustedKeysOuter->listed(signingPublic2));
1232 BEAST_EXPECT(!trustedKeysOuter->trusted(signingPublic2));
1233 BEAST_EXPECT(!trustedKeysOuter->listed(signingPublic1));
1234 BEAST_EXPECT(!trustedKeysOuter->trusted(signingPublic1));
1235 }
1236 {
1237 // Make quorum unattainable if lists from any publishers are
1238 // unavailable
1239 auto trustedKeys = std::make_unique<ValidatorList>(
1240 manifestsOuter,
1241 manifestsOuter,
1242 env.timeKeeper(),
1243 app.config().legacy("database_path"),
1244 env.journal);
1245 auto const publisherSecret = randomSecretKey();
1246 auto const publisherPublic =
1247 derivePublicKey(KeyType::ed25519, publisherSecret);
1248
1249 std::vector<std::string> cfgPublishers({strHex(publisherPublic)});
1250 std::vector<std::string> emptyCfgKeys;
1251
1252 BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgPublishers));
1253
1254 TrustChanges changes = trustedKeys->updateTrusted(
1255 activeValidatorsOuter,
1256 env.timeKeeper().now(),
1257 env.app().getOPs(),
1258 env.app().overlay(),
1259 env.app().getHashRouter());
1260 BEAST_EXPECT(changes.removed.empty());
1261 BEAST_EXPECT(changes.added.empty());
1262 BEAST_EXPECT(
1263 trustedKeys->quorum() ==
1265 }
1266 {
1267 // Should use custom minimum quorum
1268 std::size_t const minQuorum = 1;
1269 ManifestCache manifests;
1270 auto trustedKeys = std::make_unique<ValidatorList>(
1271 manifests,
1272 manifests,
1273 env.timeKeeper(),
1274 app.config().legacy("database_path"),
1275 env.journal,
1276 minQuorum);
1277
1278 std::size_t n = 10;
1280 cfgKeys.reserve(n);
1281 hash_set<NodeID> expectedTrusted;
1282 hash_set<NodeID> activeValidators;
1283 NodeID toBeSeen;
1284
1285 while (cfgKeys.size() < n)
1286 {
1287 auto const valKey = randomNode();
1288 cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey));
1289 expectedTrusted.emplace(calcNodeID(valKey));
1290 if (cfgKeys.size() < std::ceil(n * 0.8f))
1291 activeValidators.emplace(calcNodeID(valKey));
1292 else if (cfgKeys.size() < std::ceil(n * 0.8f))
1293 toBeSeen = calcNodeID(valKey);
1294 }
1295
1296 BEAST_EXPECT(trustedKeys->load({}, cfgKeys, cfgPublishersOuter));
1297
1298 TrustChanges changes = trustedKeys->updateTrusted(
1299 activeValidators,
1300 env.timeKeeper().now(),
1301 env.app().getOPs(),
1302 env.app().overlay(),
1303 env.app().getHashRouter());
1304 BEAST_EXPECT(changes.removed.empty());
1305 BEAST_EXPECT(changes.added == expectedTrusted);
1306 BEAST_EXPECT(trustedKeys->quorum() == minQuorum);
1307
1308 // Use configured quorum even when seen validators >= quorum
1309 activeValidators.emplace(toBeSeen);
1310 changes = trustedKeys->updateTrusted(
1311 activeValidators,
1312 env.timeKeeper().now(),
1313 env.app().getOPs(),
1314 env.app().overlay(),
1315 env.app().getHashRouter());
1316 BEAST_EXPECT(changes.removed.empty());
1317 BEAST_EXPECT(changes.added.empty());
1318 BEAST_EXPECT(trustedKeys->quorum() == minQuorum);
1319 }
1320 {
1321 // Remove expired published list
1322 auto trustedKeys = std::make_unique<ValidatorList>(
1323 manifestsOuter,
1324 manifestsOuter,
1325 env.app().timeKeeper(),
1326 app.config().legacy("database_path"),
1327 env.journal);
1328
1329 std::vector<std::string> emptyCfgKeys;
1330 auto const publisherKeys = randomKeyPair(KeyType::secp256k1);
1331 auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1);
1332 auto const manifest = base64_encode(makeManifestString(
1333 publisherKeys.first,
1334 publisherKeys.second,
1335 pubSigningKeys.first,
1336 pubSigningKeys.second,
1337 1));
1338
1339 std::vector<std::string> cfgKeys({strHex(publisherKeys.first)});
1340
1341 BEAST_EXPECT(trustedKeys->load({}, emptyCfgKeys, cfgKeys));
1342
1343 std::vector<Validator> list({randomValidator(), randomValidator()});
1344 hash_set<NodeID> activeValidators(
1345 asNodeIDs({list[0].masterPublic, list[1].masterPublic}));
1346
1347 // do not apply expired list
1348 auto const version = 1;
1349 auto const sequence = 1;
1350 using namespace std::chrono_literals;
1351 NetClock::time_point const validUntil =
1352 env.timeKeeper().now() + 60s;
1353 auto const blob =
1354 makeList(list, sequence, validUntil.time_since_epoch().count());
1355 auto const sig = signList(blob, pubSigningKeys);
1356
1357 BEAST_EXPECT(
1358 ListDisposition::accepted ==
1359 trustedKeys
1360 ->applyLists(manifest, version, {{blob, sig, {}}}, siteUri)
1361 .bestDisposition());
1362
1363 TrustChanges changes = trustedKeys->updateTrusted(
1364 activeValidators,
1365 env.timeKeeper().now(),
1366 env.app().getOPs(),
1367 env.app().overlay(),
1368 env.app().getHashRouter());
1369 BEAST_EXPECT(changes.removed.empty());
1370 BEAST_EXPECT(changes.added == activeValidators);
1371 for (Validator const& val : list)
1372 {
1373 BEAST_EXPECT(trustedKeys->trusted(val.masterPublic));
1374 BEAST_EXPECT(trustedKeys->trusted(val.signingPublic));
1375 }
1376 BEAST_EXPECT(trustedKeys->quorum() == 2);
1377
1378 env.timeKeeper().set(validUntil);
1379 changes = trustedKeys->updateTrusted(
1380 activeValidators,
1381 env.timeKeeper().now(),
1382 env.app().getOPs(),
1383 env.app().overlay(),
1384 env.app().getHashRouter());
1385 BEAST_EXPECT(changes.removed == activeValidators);
1386 BEAST_EXPECT(changes.added.empty());
1387 BEAST_EXPECT(!trustedKeys->trusted(list[0].masterPublic));
1388 BEAST_EXPECT(!trustedKeys->trusted(list[1].masterPublic));
1389 BEAST_EXPECT(
1390 trustedKeys->quorum() ==
1392
1393 // (Re)trust validators from new valid list
1394 std::vector<Validator> list2({list[0], randomValidator()});
1395 activeValidators.insert(calcNodeID(list2[1].masterPublic));
1396 auto const sequence2 = 2;
1397 NetClock::time_point const expiration2 =
1398 env.timeKeeper().now() + 60s;
1399 auto const blob2 = makeList(
1400 list2, sequence2, expiration2.time_since_epoch().count());
1401 auto const sig2 = signList(blob2, pubSigningKeys);
1402
1403 BEAST_EXPECT(
1404 ListDisposition::accepted ==
1405 trustedKeys
1406 ->applyLists(
1407 manifest, version, {{blob2, sig2, {}}}, siteUri)
1408 .bestDisposition());
1409
1410 changes = trustedKeys->updateTrusted(
1411 activeValidators,
1412 env.timeKeeper().now(),
1413 env.app().getOPs(),
1414 env.app().overlay(),
1415 env.app().getHashRouter());
1416 BEAST_EXPECT(changes.removed.empty());
1417 BEAST_EXPECT(
1418 changes.added ==
1419 asNodeIDs({list2[0].masterPublic, list2[1].masterPublic}));
1420 for (Validator const& val : list2)
1421 {
1422 BEAST_EXPECT(trustedKeys->trusted(val.masterPublic));
1423 BEAST_EXPECT(trustedKeys->trusted(val.signingPublic));
1424 }
1425 BEAST_EXPECT(!trustedKeys->trusted(list[1].masterPublic));
1426 BEAST_EXPECT(!trustedKeys->trusted(list[1].signingPublic));
1427 BEAST_EXPECT(trustedKeys->quorum() == 2);
1428 }
1429 {
1430 // Test 1-9 configured validators
1431 auto trustedKeys = std::make_unique<ValidatorList>(
1432 manifestsOuter,
1433 manifestsOuter,
1434 env.timeKeeper(),
1435 app.config().legacy("database_path"),
1436 env.journal);
1437
1438 std::vector<std::string> cfgPublishers;
1439 hash_set<NodeID> activeValidators;
1440 hash_set<PublicKey> activeKeys;
1441
1443 cfgKeys.reserve(9);
1444
1445 while (cfgKeys.size() < cfgKeys.capacity())
1446 {
1447 auto const valKey = randomNode();
1448 cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey));
1449 activeValidators.emplace(calcNodeID(valKey));
1450 activeKeys.emplace(valKey);
1451 BEAST_EXPECT(trustedKeys->load({}, cfgKeys, cfgPublishers));
1452 TrustChanges changes = trustedKeys->updateTrusted(
1453 activeValidators,
1454 env.timeKeeper().now(),
1455 env.app().getOPs(),
1456 env.app().overlay(),
1457 env.app().getHashRouter());
1458 BEAST_EXPECT(changes.removed.empty());
1459 BEAST_EXPECT(changes.added == asNodeIDs({valKey}));
1460 BEAST_EXPECT(
1461 trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f));
1462 for (auto const& key : activeKeys)
1463 BEAST_EXPECT(trustedKeys->trusted(key));
1464 }
1465 }
1466 {
1467 // Test 2-9 configured validators as validator
1468 auto trustedKeys = std::make_unique<ValidatorList>(
1469 manifestsOuter,
1470 manifestsOuter,
1471 env.timeKeeper(),
1472 app.config().legacy("database_path"),
1473 env.journal);
1474
1475 auto const localKey = randomNode();
1476 std::vector<std::string> cfgPublishers;
1477 hash_set<NodeID> activeValidators;
1478 hash_set<PublicKey> activeKeys;
1480 toBase58(TokenType::NodePublic, localKey)};
1481 cfgKeys.reserve(9);
1482
1483 while (cfgKeys.size() < cfgKeys.capacity())
1484 {
1485 auto const valKey = randomNode();
1486 cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey));
1487 activeValidators.emplace(calcNodeID(valKey));
1488 activeKeys.emplace(valKey);
1489
1490 BEAST_EXPECT(
1491 trustedKeys->load(localKey, cfgKeys, cfgPublishers));
1492 TrustChanges changes = trustedKeys->updateTrusted(
1493 activeValidators,
1494 env.timeKeeper().now(),
1495 env.app().getOPs(),
1496 env.app().overlay(),
1497 env.app().getHashRouter());
1498 BEAST_EXPECT(changes.removed.empty());
1499 if (cfgKeys.size() > 2)
1500 BEAST_EXPECT(changes.added == asNodeIDs({valKey}));
1501 else
1502 BEAST_EXPECT(
1503 changes.added == asNodeIDs({localKey, valKey}));
1504
1505 BEAST_EXPECT(
1506 trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f));
1507
1508 for (auto const& key : activeKeys)
1509 BEAST_EXPECT(trustedKeys->trusted(key));
1510 }
1511 }
1512 {
1513 // Trusted set should include all validators from multiple lists
1514 ManifestCache manifests;
1515 auto trustedKeys = std::make_unique<ValidatorList>(
1516 manifests,
1517 manifests,
1518 env.timeKeeper(),
1519 app.config().legacy("database_path"),
1520 env.journal);
1521
1522 hash_set<NodeID> activeValidators;
1523 std::vector<Validator> valKeys;
1524 valKeys.reserve(maxKeys);
1525
1526 while (valKeys.size() != maxKeys)
1527 {
1528 valKeys.push_back(randomValidator());
1529 activeValidators.emplace(
1530 calcNodeID(valKeys.back().masterPublic));
1531 }
1532
1533 auto addPublishedList = [this,
1534 &env,
1535 &trustedKeys,
1536 &valKeys,
1537 &siteUri]() {
1538 auto const publisherSecret = randomSecretKey();
1539 auto const publisherPublic =
1540 derivePublicKey(KeyType::ed25519, publisherSecret);
1541 auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1);
1542 auto const manifest = base64_encode(makeManifestString(
1543 publisherPublic,
1544 publisherSecret,
1545 pubSigningKeys.first,
1546 pubSigningKeys.second,
1547 1));
1548
1549 std::vector<std::string> cfgPublishers(
1550 {strHex(publisherPublic)});
1551 std::vector<std::string> emptyCfgKeys;
1552
1553 BEAST_EXPECT(
1554 trustedKeys->load({}, emptyCfgKeys, cfgPublishers));
1555
1556 auto const version = 1;
1557 auto const sequence = 1;
1558 using namespace std::chrono_literals;
1559 NetClock::time_point const validUntil =
1560 env.timeKeeper().now() + 3600s;
1561 auto const blob = makeList(
1562 valKeys, sequence, validUntil.time_since_epoch().count());
1563 auto const sig = signList(blob, pubSigningKeys);
1564
1565 BEAST_EXPECT(
1566 ListDisposition::accepted ==
1567 trustedKeys
1568 ->applyLists(
1569 manifest, version, {{blob, sig, {}}}, siteUri)
1570 .bestDisposition());
1571 };
1572
1573 // Apply multiple published lists
1574 for (auto i = 0; i < 3; ++i)
1575 addPublishedList();
1576
1577 TrustChanges changes = trustedKeys->updateTrusted(
1578 activeValidators,
1579 env.timeKeeper().now(),
1580 env.app().getOPs(),
1581 env.app().overlay(),
1582 env.app().getHashRouter());
1583
1584 BEAST_EXPECT(
1585 trustedKeys->quorum() == std::ceil(valKeys.size() * 0.8f));
1586
1587 hash_set<NodeID> added;
1588 for (auto const& val : valKeys)
1589 {
1590 BEAST_EXPECT(trustedKeys->trusted(val.masterPublic));
1591 added.insert(calcNodeID(val.masterPublic));
1592 }
1593 BEAST_EXPECT(changes.added == added);
1594 BEAST_EXPECT(changes.removed.empty());
1595 }
1596 }
1597
1598 void
1600 {
1601 testcase("Expires");
1602
1603 std::string const siteUri = "testExpires.test";
1604
1605 jtx::Env env(*this);
1606 auto& app = env.app();
1607
1608 auto toStr = [](PublicKey const& publicKey) {
1609 return toBase58(TokenType::NodePublic, publicKey);
1610 };
1611
1612 // Config listed keys
1613 {
1614 ManifestCache manifests;
1615 auto trustedKeys = std::make_unique<ValidatorList>(
1616 manifests,
1617 manifests,
1618 env.timeKeeper(),
1619 app.config().legacy("database_path"),
1620 env.journal);
1621
1622 // Empty list has no expiration
1623 BEAST_EXPECT(trustedKeys->expires() == std::nullopt);
1624
1625 // Config listed keys have maximum expiry
1626 PublicKey localCfgListed = randomNode();
1627 trustedKeys->load({}, {toStr(localCfgListed)}, {});
1628 BEAST_EXPECT(
1629 trustedKeys->expires() &&
1630 trustedKeys->expires().value() == NetClock::time_point::max());
1631 BEAST_EXPECT(trustedKeys->listed(localCfgListed));
1632 }
1633
1634 // Published keys with expirations
1635 {
1636 ManifestCache manifests;
1637 auto trustedKeys = std::make_unique<ValidatorList>(
1638 manifests,
1639 manifests,
1640 env.app().timeKeeper(),
1641 app.config().legacy("database_path"),
1642 env.journal);
1643
1644 std::vector<Validator> validators = {randomValidator()};
1645 hash_set<NodeID> activeValidators;
1646 for (Validator const& val : validators)
1647 activeValidators.insert(calcNodeID(val.masterPublic));
1648 // Store prepared list data to control when it is applied
1649 struct PreparedList
1650 {
1651 PublicKey publisherPublic;
1654 int version;
1656 };
1657
1658 using namespace std::chrono_literals;
1659 auto addPublishedList = [this, &env, &trustedKeys, &validators]() {
1660 auto const publisherSecret = randomSecretKey();
1661 auto const publisherPublic =
1662 derivePublicKey(KeyType::ed25519, publisherSecret);
1663 auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1);
1664 auto const manifest = base64_encode(makeManifestString(
1665 publisherPublic,
1666 publisherSecret,
1667 pubSigningKeys.first,
1668 pubSigningKeys.second,
1669 1));
1670
1671 std::vector<std::string> cfgPublishers(
1672 {strHex(publisherPublic)});
1673 std::vector<std::string> emptyCfgKeys;
1674
1675 BEAST_EXPECT(
1676 trustedKeys->load({}, emptyCfgKeys, cfgPublishers));
1677
1678 auto const version = 2;
1679 auto const sequence1 = 1;
1680 NetClock::time_point const expiration1 =
1681 env.timeKeeper().now() + 1800s;
1682 auto const blob1 = makeList(
1683 validators,
1684 sequence1,
1685 expiration1.time_since_epoch().count());
1686 auto const sig1 = signList(blob1, pubSigningKeys);
1687
1688 NetClock::time_point const effective2 = expiration1 - 300s;
1689 NetClock::time_point const expiration2 = effective2 + 1800s;
1690 auto const sequence2 = 2;
1691 auto const blob2 = makeList(
1692 validators,
1693 sequence2,
1694 expiration2.time_since_epoch().count(),
1695 effective2.time_since_epoch().count());
1696 auto const sig2 = signList(blob2, pubSigningKeys);
1697
1698 return PreparedList{
1699 publisherPublic,
1700 manifest,
1701 {{blob1, sig1, {}}, {blob2, sig2, {}}},
1702 version,
1703 {expiration1, expiration2}};
1704 };
1705
1706 // Configure two publishers and prepare 2 lists
1707 PreparedList prep1 = addPublishedList();
1708 env.timeKeeper().set(env.timeKeeper().now() + 200s);
1709 PreparedList prep2 = addPublishedList();
1710
1711 // Initially, no list has been published, so no known expiration
1712 BEAST_EXPECT(trustedKeys->expires() == std::nullopt);
1713
1714 // Apply first list
1715 checkResult(
1716 trustedKeys->applyLists(
1717 prep1.manifest, prep1.version, prep1.blobs, siteUri),
1718 prep1.publisherPublic,
1719 ListDisposition::pending,
1720 ListDisposition::accepted);
1721
1722 // One list still hasn't published, so expiration is still
1723 // unknown
1724 BEAST_EXPECT(trustedKeys->expires() == std::nullopt);
1725
1726 // Apply second list
1727 checkResult(
1728 trustedKeys->applyLists(
1729 prep2.manifest, prep2.version, prep2.blobs, siteUri),
1730 prep2.publisherPublic,
1731 ListDisposition::pending,
1732 ListDisposition::accepted);
1733 // We now have loaded both lists, so expiration is known
1734 BEAST_EXPECT(
1735 trustedKeys->expires() &&
1736 trustedKeys->expires().value() == prep1.expirations.back());
1737
1738 // Advance past the first list's LAST validFrom date. It remains
1739 // the earliest validUntil, while rotating in the second list
1740 {
1741 env.timeKeeper().set(prep1.expirations.front() - 1s);
1742 auto changes = trustedKeys->updateTrusted(
1743 activeValidators,
1744 env.timeKeeper().now(),
1745 env.app().getOPs(),
1746 env.app().overlay(),
1747 env.app().getHashRouter());
1748 BEAST_EXPECT(
1749 trustedKeys->expires() &&
1750 trustedKeys->expires().value() == prep1.expirations.back());
1751 BEAST_EXPECT(!changes.added.empty());
1752 BEAST_EXPECT(changes.removed.empty());
1753 }
1754
1755 // Advance past the first list's LAST validUntil, but it remains
1756 // the earliest validUntil, while being invalidated
1757 {
1758 env.timeKeeper().set(prep1.expirations.back() + 1s);
1759 auto changes = trustedKeys->updateTrusted(
1760 activeValidators,
1761 env.timeKeeper().now(),
1762 env.app().getOPs(),
1763 env.app().overlay(),
1764 env.app().getHashRouter());
1765 BEAST_EXPECT(
1766 trustedKeys->expires() &&
1767 trustedKeys->expires().value() == prep1.expirations.back());
1768 BEAST_EXPECT(changes.added.empty());
1769 BEAST_EXPECT(changes.removed.empty());
1770 }
1771 }
1772 }
1773
1774 void
1776 {
1777 testcase("NegativeUNL");
1778 jtx::Env env(*this);
1779 ManifestCache manifests;
1780
1781 auto createValidatorList =
1782 [&](std::uint32_t vlSize,
1783 std::optional<std::size_t> minimumQuorum = {})
1785 auto trustedKeys = std::make_shared<ValidatorList>(
1786 manifests,
1787 manifests,
1788 env.timeKeeper(),
1789 env.app().config().legacy("database_path"),
1790 env.journal,
1791 minimumQuorum);
1792
1793 std::vector<std::string> cfgPublishers;
1795 hash_set<NodeID> activeValidators;
1796 cfgKeys.reserve(vlSize);
1797 while (cfgKeys.size() < cfgKeys.capacity())
1798 {
1799 auto const valKey = randomNode();
1800 cfgKeys.push_back(toBase58(TokenType::NodePublic, valKey));
1801 activeValidators.emplace(calcNodeID(valKey));
1802 }
1803 if (trustedKeys->load({}, cfgKeys, cfgPublishers))
1804 {
1805 trustedKeys->updateTrusted(
1806 activeValidators,
1807 env.timeKeeper().now(),
1808 env.app().getOPs(),
1809 env.app().overlay(),
1810 env.app().getHashRouter());
1811 if (minimumQuorum == trustedKeys->quorum() ||
1812 trustedKeys->quorum() == std::ceil(cfgKeys.size() * 0.8f))
1813 return trustedKeys;
1814 }
1815 return nullptr;
1816 };
1817
1818 /*
1819 * Test NegativeUNL
1820 * == Combinations ==
1821 * -- UNL size: 34, 35, 57
1822 * -- nUNL size: 0%, 20%, 30%, 50%
1823 *
1824 * == with UNL size 60
1825 * -- set == get,
1826 * -- check quorum, with nUNL size: 0, 12, 30, 18
1827 * -- nUNL overlap: |nUNL - UNL| = 5, with nUNL size: 18
1828 * -- with command line minimumQuorum = 50%,
1829 * seen_reliable affected by nUNL
1830 */
1831
1832 {
1833 hash_set<NodeID> activeValidators;
1834 //== Combinations ==
1835 std::array<std::uint32_t, 4> unlSizes = {34, 35, 39, 60};
1836 std::array<std::uint32_t, 4> nUnlPercent = {0, 20, 30, 50};
1837 for (auto us : unlSizes)
1838 {
1839 for (auto np : nUnlPercent)
1840 {
1841 auto validators = createValidatorList(us);
1842 BEAST_EXPECT(validators);
1843 if (validators)
1844 {
1845 std::uint32_t nUnlSize = us * np / 100;
1846 auto unl = validators->getTrustedMasterKeys();
1848 auto it = unl.begin();
1849 for (std::uint32_t i = 0; i < nUnlSize; ++i)
1850 {
1851 nUnl.insert(*it);
1852 ++it;
1853 }
1854 validators->setNegativeUNL(nUnl);
1855 validators->updateTrusted(
1856 activeValidators,
1857 env.timeKeeper().now(),
1858 env.app().getOPs(),
1859 env.app().overlay(),
1860 env.app().getHashRouter());
1861 BEAST_EXPECT(
1862 validators->quorum() ==
1863 static_cast<std::size_t>(std::ceil(
1864 std::max((us - nUnlSize) * 0.8f, us * 0.6f))));
1865 }
1866 }
1867 }
1868 }
1869
1870 {
1871 //== with UNL size 60
1872 auto validators = createValidatorList(60);
1873 BEAST_EXPECT(validators);
1874 if (validators)
1875 {
1876 hash_set<NodeID> activeValidators;
1877 auto unl = validators->getTrustedMasterKeys();
1878 BEAST_EXPECT(unl.size() == 60);
1879 {
1880 //-- set == get,
1881 //-- check quorum, with nUNL size: 0, 30, 18, 12
1882 auto nUnlChange = [&](std::uint32_t nUnlSize,
1883 std::uint32_t quorum) -> bool {
1885 auto it = unl.begin();
1886 for (std::uint32_t i = 0; i < nUnlSize; ++i)
1887 {
1888 nUnl.insert(*it);
1889 ++it;
1890 }
1891 validators->setNegativeUNL(nUnl);
1892 auto nUnl_temp = validators->getNegativeUNL();
1893 if (nUnl_temp.size() == nUnl.size())
1894 {
1895 for (auto& n : nUnl_temp)
1896 {
1897 if (nUnl.find(n) == nUnl.end())
1898 return false;
1899 }
1900 validators->updateTrusted(
1901 activeValidators,
1902 env.timeKeeper().now(),
1903 env.app().getOPs(),
1904 env.app().overlay(),
1905 env.app().getHashRouter());
1906 return validators->quorum() == quorum;
1907 }
1908 return false;
1909 };
1910 BEAST_EXPECT(nUnlChange(0, 48));
1911 BEAST_EXPECT(nUnlChange(30, 36));
1912 BEAST_EXPECT(nUnlChange(18, 36));
1913 BEAST_EXPECT(nUnlChange(12, 39));
1914 }
1915
1916 {
1917 // nUNL overlap: |nUNL - UNL| = 5, with nUNL size:
1918 // 18
1919 auto nUnl = validators->getNegativeUNL();
1920 BEAST_EXPECT(nUnl.size() == 12);
1921 std::size_t ss = 33;
1922 std::vector<uint8_t> data(ss, 0);
1923 data[0] = 0xED;
1924 for (int i = 0; i < 6; ++i)
1925 {
1926 Slice s(data.data(), ss);
1927 data[1]++;
1928 nUnl.emplace(s);
1929 }
1930 validators->setNegativeUNL(nUnl);
1931 validators->updateTrusted(
1932 activeValidators,
1933 env.timeKeeper().now(),
1934 env.app().getOPs(),
1935 env.app().overlay(),
1936 env.app().getHashRouter());
1937 BEAST_EXPECT(validators->quorum() == 39);
1938 }
1939 }
1940 }
1941
1942 {
1943 //== with UNL size 60
1944 //-- with command line minimumQuorum = 50%,
1945 // seen_reliable affected by nUNL
1946 auto validators = createValidatorList(60, 30);
1947 BEAST_EXPECT(validators);
1948 if (validators)
1949 {
1950 hash_set<NodeID> activeValidators;
1951 hash_set<PublicKey> unl = validators->getTrustedMasterKeys();
1952 auto it = unl.begin();
1953 for (std::uint32_t i = 0; i < 50; ++i)
1954 {
1955 activeValidators.insert(calcNodeID(*it));
1956 ++it;
1957 }
1958 validators->updateTrusted(
1959 activeValidators,
1960 env.timeKeeper().now(),
1961 env.app().getOPs(),
1962 env.app().overlay(),
1963 env.app().getHashRouter());
1964 BEAST_EXPECT(validators->quorum() == 30);
1966 it = unl.begin();
1967 for (std::uint32_t i = 0; i < 20; ++i)
1968 {
1969 nUnl.insert(*it);
1970 ++it;
1971 }
1972 validators->setNegativeUNL(nUnl);
1973 validators->updateTrusted(
1974 activeValidators,
1975 env.timeKeeper().now(),
1976 env.app().getOPs(),
1977 env.app().overlay(),
1978 env.app().getHashRouter());
1979 BEAST_EXPECT(validators->quorum() == 30);
1980 }
1981 }
1982 }
1983
1984 void
1986 {
1987 testcase("Sha512 hashing");
1988 // Tests that ValidatorList hash_append helpers with a single blob
1989 // returns the same result as ripple::Sha512Half used by the
1990 // TMValidatorList protocol message handler
1991 std::string const manifest = "This is not really a manifest";
1992 std::string const blob = "This is not really a blob";
1993 std::string const signature = "This is not really a signature";
1994 std::uint32_t const version = 1;
1995
1996 auto const global = sha512Half(manifest, blob, signature, version);
1997 BEAST_EXPECT(!!global);
1998
1999 std::vector<ValidatorBlobInfo> blobVector(1);
2000 blobVector[0].blob = blob;
2001 blobVector[0].signature = signature;
2002 BEAST_EXPECT(global == sha512Half(manifest, blobVector, version));
2003 BEAST_EXPECT(global != sha512Half(signature, blobVector, version));
2004
2005 {
2007 {99, blobVector[0]}};
2008 BEAST_EXPECT(global == sha512Half(manifest, blobMap, version));
2009 BEAST_EXPECT(global != sha512Half(blob, blobMap, version));
2010 }
2011
2012 {
2013 protocol::TMValidatorList msg1;
2014 msg1.set_manifest(manifest);
2015 msg1.set_blob(blob);
2016 msg1.set_signature(signature);
2017 msg1.set_version(version);
2018 BEAST_EXPECT(global == sha512Half(msg1));
2019 msg1.set_signature(blob);
2020 BEAST_EXPECT(global != sha512Half(msg1));
2021 }
2022
2023 {
2024 protocol::TMValidatorListCollection msg2;
2025 msg2.set_manifest(manifest);
2026 msg2.set_version(version);
2027 auto& bi = *msg2.add_blobs();
2028 bi.set_blob(blob);
2029 bi.set_signature(signature);
2030 BEAST_EXPECT(global == sha512Half(msg2));
2031 bi.set_manifest(manifest);
2032 BEAST_EXPECT(global != sha512Half(msg2));
2033 }
2034 }
2035
2036 void
2038 {
2039 testcase("Build and split messages");
2040
2041 std::uint32_t const manifestCutoff = 7;
2042 auto extractHeader = [this](Message& message) {
2043 auto const& buffer =
2044 message.getBuffer(compression::Compressed::Off);
2045
2046 boost::beast::multi_buffer buffers;
2047
2048 // simulate multi-buffer
2049 auto start = buffer.begin();
2050 auto end = buffer.end();
2051 std::vector<std::uint8_t> slice(start, end);
2052 buffers.commit(boost::asio::buffer_copy(
2053 buffers.prepare(slice.size()), boost::asio::buffer(slice)));
2054
2055 boost::system::error_code ec;
2056 auto header =
2057 detail::parseMessageHeader(ec, buffers.data(), buffers.size());
2058 BEAST_EXPECT(!ec);
2059 return std::make_pair(header, buffers);
2060 };
2061 auto extractProtocolMessage1 = [this,
2062 &extractHeader](Message& message) {
2063 auto [header, buffers] = extractHeader(message);
2064 if (BEAST_EXPECT(header) &&
2065 BEAST_EXPECT(header->message_type == protocol::mtVALIDATORLIST))
2066 {
2067 auto const msg =
2068 detail::parseMessageContent<protocol::TMValidatorList>(
2069 *header, buffers.data());
2070 BEAST_EXPECT(msg);
2071 return msg;
2072 }
2074 };
2075 auto extractProtocolMessage2 = [this,
2076 &extractHeader](Message& message) {
2077 auto [header, buffers] = extractHeader(message);
2078 if (BEAST_EXPECT(header) &&
2079 BEAST_EXPECT(
2080 header->message_type ==
2081 protocol::mtVALIDATORLISTCOLLECTION))
2082 {
2083 auto const msg = detail::parseMessageContent<
2084 protocol::TMValidatorListCollection>(
2085 *header, buffers.data());
2086 BEAST_EXPECT(msg);
2087 return msg;
2088 }
2090 };
2091 auto verifyMessage =
2092 [this,
2093 manifestCutoff,
2094 &extractProtocolMessage1,
2095 &extractProtocolMessage2](
2096 auto const version,
2097 auto const& manifest,
2098 auto const& blobInfos,
2099 auto const& messages,
2101 expectedInfo) {
2102 BEAST_EXPECT(messages.size() == expectedInfo.size());
2103 auto msgIter = expectedInfo.begin();
2104 for (auto const& messageWithHash : messages)
2105 {
2106 if (!BEAST_EXPECT(msgIter != expectedInfo.end()))
2107 break;
2108 if (!BEAST_EXPECT(messageWithHash.message))
2109 continue;
2110 auto const& expectedSeqs = msgIter->second;
2111 auto seqIter = expectedSeqs.begin();
2112 auto const size =
2113 messageWithHash.message
2114 ->getBuffer(compression::Compressed::Off)
2115 .size();
2116 // This size is arbitrary, but shouldn't change
2117 BEAST_EXPECT(size == msgIter->first);
2118 if (expectedSeqs.size() == 1)
2119 {
2120 auto const msg =
2121 extractProtocolMessage1(*messageWithHash.message);
2122 auto const expectedVersion = 1;
2123 if (BEAST_EXPECT(msg))
2124 {
2125 BEAST_EXPECT(msg->version() == expectedVersion);
2126 if (!BEAST_EXPECT(seqIter != expectedSeqs.end()))
2127 continue;
2128 auto const& expectedBlob = blobInfos.at(*seqIter);
2129 BEAST_EXPECT(
2130 (*seqIter < manifestCutoff) ==
2131 !!expectedBlob.manifest);
2132 auto const expectedManifest =
2133 *seqIter < manifestCutoff &&
2134 expectedBlob.manifest
2135 ? *expectedBlob.manifest
2136 : manifest;
2137 BEAST_EXPECT(msg->manifest() == expectedManifest);
2138 BEAST_EXPECT(msg->blob() == expectedBlob.blob);
2139 BEAST_EXPECT(
2140 msg->signature() == expectedBlob.signature);
2141 ++seqIter;
2142 BEAST_EXPECT(seqIter == expectedSeqs.end());
2143
2144 BEAST_EXPECT(
2145 messageWithHash.hash ==
2146 sha512Half(
2147 expectedManifest,
2148 expectedBlob.blob,
2149 expectedBlob.signature,
2150 expectedVersion));
2151 }
2152 }
2153 else
2154 {
2155 std::vector<ValidatorBlobInfo> hashingBlobs;
2156 hashingBlobs.reserve(msgIter->second.size());
2157
2158 auto const msg =
2159 extractProtocolMessage2(*messageWithHash.message);
2160 if (BEAST_EXPECT(msg))
2161 {
2162 BEAST_EXPECT(msg->version() == version);
2163 BEAST_EXPECT(msg->manifest() == manifest);
2164 for (auto const& blobInfo : msg->blobs())
2165 {
2166 if (!BEAST_EXPECT(
2167 seqIter != expectedSeqs.end()))
2168 break;
2169 auto const& expectedBlob =
2170 blobInfos.at(*seqIter);
2171 hashingBlobs.push_back(expectedBlob);
2172 BEAST_EXPECT(
2173 blobInfo.has_manifest() ==
2174 !!expectedBlob.manifest);
2175 BEAST_EXPECT(
2176 blobInfo.has_manifest() ==
2177 (*seqIter < manifestCutoff));
2178
2179 if (*seqIter < manifestCutoff)
2180 BEAST_EXPECT(
2181 blobInfo.manifest() ==
2182 *expectedBlob.manifest);
2183 BEAST_EXPECT(
2184 blobInfo.blob() == expectedBlob.blob);
2185 BEAST_EXPECT(
2186 blobInfo.signature() ==
2187 expectedBlob.signature);
2188 ++seqIter;
2189 }
2190 BEAST_EXPECT(seqIter == expectedSeqs.end());
2191 }
2192 BEAST_EXPECT(
2193 messageWithHash.hash ==
2194 sha512Half(manifest, hashingBlobs, version));
2195 }
2196 ++msgIter;
2197 }
2198 BEAST_EXPECT(msgIter == expectedInfo.end());
2199 };
2200 auto verifyBuildMessages =
2201 [this](
2203 std::size_t expectedSequence,
2204 std::size_t expectedSize) {
2205 BEAST_EXPECT(result.first == expectedSequence);
2206 BEAST_EXPECT(result.second == expectedSize);
2207 };
2208
2209 std::string const manifest = "This is not a manifest";
2210 std::uint32_t const version = 2;
2211 // Mutable so items can be removed in later tests.
2212 auto const blobInfos = [manifestCutoff = manifestCutoff]() {
2214
2215 for (auto seq : {5, 6, 7, 10, 12})
2216 {
2217 auto& b = bis[seq];
2219 s << "This is not a blob with sequence " << seq;
2220 b.blob = s.str();
2221 s.str(std::string());
2222 s << "This is not a signature for sequence " << seq;
2223 b.signature = s.str();
2224 if (seq < manifestCutoff)
2225 {
2226 // add a manifest for the "early" blobs
2227 s.str(std::string());
2228 s << "This is not manifest " << seq;
2229 b.manifest = s.str();
2230 }
2231 }
2232 return bis;
2233 }();
2234 auto const maxSequence = blobInfos.rbegin()->first;
2235 BEAST_EXPECT(maxSequence == 12);
2236
2238
2239 // Version 1
2240
2241 // This peer has a VL ahead of our "current"
2242 verifyBuildMessages(
2243 ValidatorList::buildValidatorListMessages(
2244 1, 8, maxSequence, version, manifest, blobInfos, messages),
2245 0,
2246 0);
2247 BEAST_EXPECT(messages.size() == 0);
2248
2249 // Don't repeat the work if messages is populated, even though the
2250 // peerSequence provided indicates it should. Note that this
2251 // situation is contrived for this test and should never happen in
2252 // real code.
2253 messages.emplace_back();
2254 verifyBuildMessages(
2255 ValidatorList::buildValidatorListMessages(
2256 1, 3, maxSequence, version, manifest, blobInfos, messages),
2257 5,
2258 0);
2259 BEAST_EXPECT(messages.size() == 1 && !messages.front().message);
2260
2261 // Generate a version 1 message
2262 messages.clear();
2263 verifyBuildMessages(
2264 ValidatorList::buildValidatorListMessages(
2265 1, 3, maxSequence, version, manifest, blobInfos, messages),
2266 5,
2267 1);
2268 if (BEAST_EXPECT(messages.size() == 1) &&
2269 BEAST_EXPECT(messages.front().message))
2270 {
2271 auto const& messageWithHash = messages.front();
2272 auto const msg = extractProtocolMessage1(*messageWithHash.message);
2273 auto const size =
2274 messageWithHash.message->getBuffer(compression::Compressed::Off)
2275 .size();
2276 // This size is arbitrary, but shouldn't change
2277 BEAST_EXPECT(size == 108);
2278 auto const& expected = blobInfos.at(5);
2279 if (BEAST_EXPECT(msg))
2280 {
2281 BEAST_EXPECT(msg->version() == 1);
2282 BEAST_EXPECT(msg->manifest() == *expected.manifest);
2283 BEAST_EXPECT(msg->blob() == expected.blob);
2284 BEAST_EXPECT(msg->signature() == expected.signature);
2285 }
2286 BEAST_EXPECT(
2287 messageWithHash.hash ==
2288 sha512Half(
2289 *expected.manifest, expected.blob, expected.signature, 1));
2290 }
2291
2292 // Version 2
2293
2294 messages.clear();
2295
2296 // This peer has a VL ahead of us.
2297 verifyBuildMessages(
2298 ValidatorList::buildValidatorListMessages(
2299 2,
2300 maxSequence * 2,
2301 maxSequence,
2302 version,
2303 manifest,
2304 blobInfos,
2305 messages),
2306 0,
2307 0);
2308 BEAST_EXPECT(messages.size() == 0);
2309
2310 // Don't repeat the work if messages is populated, even though the
2311 // peerSequence provided indicates it should. Note that this
2312 // situation is contrived for this test and should never happen in
2313 // real code.
2314 messages.emplace_back();
2315 verifyBuildMessages(
2316 ValidatorList::buildValidatorListMessages(
2317 2, 3, maxSequence, version, manifest, blobInfos, messages),
2318 maxSequence,
2319 0);
2320 BEAST_EXPECT(messages.size() == 1 && !messages.front().message);
2321
2322 // Generate a version 2 message. Don't send the current
2323 messages.clear();
2324 verifyBuildMessages(
2325 ValidatorList::buildValidatorListMessages(
2326 2, 5, maxSequence, version, manifest, blobInfos, messages),
2327 maxSequence,
2328 4);
2329 verifyMessage(
2330 version, manifest, blobInfos, messages, {{372, {6, 7, 10, 12}}});
2331
2332 // Test message splitting on size limits.
2333
2334 // Set a limit that should give two messages
2335 messages.clear();
2336 verifyBuildMessages(
2337 ValidatorList::buildValidatorListMessages(
2338 2, 5, maxSequence, version, manifest, blobInfos, messages, 300),
2339 maxSequence,
2340 4);
2341 verifyMessage(
2342 version,
2343 manifest,
2344 blobInfos,
2345 messages,
2346 {{212, {6, 7}}, {192, {10, 12}}});
2347
2348 // Set a limit between the size of the two earlier messages so one
2349 // will split and the other won't
2350 messages.clear();
2351 verifyBuildMessages(
2352 ValidatorList::buildValidatorListMessages(
2353 2, 5, maxSequence, version, manifest, blobInfos, messages, 200),
2354 maxSequence,
2355 4);
2356 verifyMessage(
2357 version,
2358 manifest,
2359 blobInfos,
2360 messages,
2361 {{108, {6}}, {108, {7}}, {192, {10, 12}}});
2362
2363 // Set a limit so that all the VLs are sent individually
2364 messages.clear();
2365 verifyBuildMessages(
2366 ValidatorList::buildValidatorListMessages(
2367 2, 5, maxSequence, version, manifest, blobInfos, messages, 150),
2368 maxSequence,
2369 4);
2370 verifyMessage(
2371 version,
2372 manifest,
2373 blobInfos,
2374 messages,
2375 {{108, {6}}, {108, {7}}, {110, {10}}, {110, {12}}});
2376
2377 // Set a limit smaller than some of the messages. Because single
2378 // messages send regardless, they will all still be sent
2379 messages.clear();
2380 verifyBuildMessages(
2381 ValidatorList::buildValidatorListMessages(
2382 2, 5, maxSequence, version, manifest, blobInfos, messages, 108),
2383 maxSequence,
2384 4);
2385 verifyMessage(
2386 version,
2387 manifest,
2388 blobInfos,
2389 messages,
2390 {{108, {6}}, {108, {7}}, {110, {10}}, {110, {12}}});
2391 }
2392
2393public:
2394 void
2395 run() override
2396 {
2397 testGenesisQuorum();
2398 testConfigLoad();
2399 testApplyLists();
2400 testGetAvailable();
2401 testUpdateTrusted();
2402 testExpires();
2403 testNegativeUNL();
2404 testSha512Hash();
2405 testBuildMessages();
2406 }
2407}; // namespace test
2408
2409BEAST_DEFINE_TESTSUITE(ValidatorList, app, ripple);
2410
2411} // namespace test
2412} // namespace ripple
T at(T... args)
T back(T... args)
T begin(T... args)
T capacity(T... args)
T ceil(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 HashRouter & getHashRouter()=0
void legacy(std::string const &section, std::string value)
Set a value that is not a key/value pair.
Remembers manifests with the highest sequence number.
Definition: Manifest.h:256
std::optional< PublicKey > getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
A public key.
Definition: PublicKey.h:62
void add(Serializer &s) const override
Definition: STObject.cpp:111
A secret key.
Definition: SecretKey.h:37
std::size_t size() const noexcept
Definition: Serializer.h:72
void const * data() const noexcept
Definition: Serializer.h:78
An immutable linear range of bytes.
Definition: Slice.h:45
time_point now() const override
Returns the current time.
void run() override
Runs the suite.
static hash_set< NodeID > asNodeIDs(std::initializer_list< PublicKey > const &pks)
void checkResult(ValidatorList::PublisherListStats const &result, PublicKey pubKey, ListDisposition expectedWorst, ListDisposition expectedBest)
static std::string makeRevocationString(PublicKey const &pk, SecretKey const &sk)
std::string makeList(std::vector< Validator > const &validators, std::size_t sequence, std::size_t validUntil, std::optional< std::size_t > validFrom={})
static std::string makeManifestString(PublicKey const &pk, SecretKey const &sk, PublicKey const &spk, SecretKey const &ssk, int seq)
std::string signList(std::string const &blob, std::pair< PublicKey, SecretKey > const &keys)
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
Set the regular signature on a JTx.
Definition: sig.h:35
T clear(T... args)
T emplace(T... args)
T end(T... args)
T find(T... args)
T insert(T... args)
T make_pair(T... args)
T max(T... args)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
void sign(Json::Value &jv, Account const &account)
Sign automatically.
Definition: utility.cpp:46
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::optional< Manifest > deserializeManifest(Slice s, beast::Journal journal)
Constructs Manifest from serialized string.
std::string base64_decode(std::string_view data)
Definition: base64.cpp:245
SField const sfGeneric
@ accepted
List is valid.
@ same_sequence
Same sequence as current list.
@ expired
List is expired, but has the largest non-pending sequence seen so far.
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
Definition: SecretKey.cpp:313
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
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
std::string base64_encode(std::uint8_t const *data, std::size_t len)
Definition: base64.cpp:236
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
Definition: PublicKey.cpp:303
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:368
@ manifest
Manifest.
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:223
T push_back(T... args)
T rbegin(T... args)
T reserve(T... args)
T str(T... args)
Changes in trusted nodes after updating validator list.
hash_set< NodeID > added
hash_set< NodeID > removed
Describes the result of processing a Validator List (UNL), including some of the information from the...
std::optional< PublicKey > publisherKey
Set the sequence number on a JTx.
Definition: seq.h:34
T time_since_epoch(T... args)
T to_string(T... args)