mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Make minimum quorum Byzantine fault tolerant (RIPD-1461)
This commit is contained in:
@@ -124,21 +124,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCalculateQuorum ()
|
||||
{
|
||||
testcase ("Calculate Quorum");
|
||||
|
||||
for(std::size_t i = 1; i < 20; ++i)
|
||||
{
|
||||
auto const quorum = ValidatorList::calculateQuorum(i);
|
||||
if (i < 10)
|
||||
BEAST_EXPECT(quorum >= (i/2 + 1));
|
||||
else
|
||||
BEAST_EXPECT(quorum == std::ceil (i * 0.8));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testConfigLoad ()
|
||||
{
|
||||
@@ -522,7 +507,7 @@ private:
|
||||
// onConsensusStart should make all available configured
|
||||
// validators trusted
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 12);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 14);
|
||||
std::size_t i = 0;
|
||||
for (auto const& val : cfgKeys)
|
||||
{
|
||||
@@ -538,6 +523,16 @@ private:
|
||||
else
|
||||
fail ();
|
||||
}
|
||||
|
||||
{
|
||||
// Quorum should be 80% with all listed validators active
|
||||
hash_set<PublicKey> activeValidators;
|
||||
for (auto const valKey : cfgKeys)
|
||||
activeValidators.emplace (*parseBase58<PublicKey>(
|
||||
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == cfgKeys.size() * 4/5);
|
||||
}
|
||||
}
|
||||
{
|
||||
// update with manifests
|
||||
@@ -571,7 +566,7 @@ private:
|
||||
manifests.applyManifest(std::move (*m1)) ==
|
||||
ManifestDisposition::accepted);
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 13);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 15);
|
||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
||||
BEAST_EXPECT(trustedKeys->listed (signingPublic1));
|
||||
@@ -584,12 +579,11 @@ private:
|
||||
auto m2 = Manifest::make_Manifest (makeManifestString (
|
||||
masterPublic, masterPrivate,
|
||||
signingPublic2, signingKeys2.second, 2));
|
||||
|
||||
BEAST_EXPECT(
|
||||
manifests.applyManifest(std::move (*m2)) ==
|
||||
ManifestDisposition::accepted);
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 13);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 15);
|
||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
||||
BEAST_EXPECT(trustedKeys->listed (signingPublic2));
|
||||
@@ -613,7 +607,7 @@ private:
|
||||
BEAST_EXPECT(manifests.getSigningKey (masterPublic) == masterPublic);
|
||||
BEAST_EXPECT(manifests.revoked (masterPublic));
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 12);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 15);
|
||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||
BEAST_EXPECT(!trustedKeys->trusted (masterPublic));
|
||||
BEAST_EXPECT(!trustedKeys->listed (signingPublicMax));
|
||||
@@ -700,7 +694,7 @@ private:
|
||||
localKey, cfgKeys, cfgPublishers));
|
||||
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 3);
|
||||
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
||||
|
||||
// local validator key is always trusted
|
||||
BEAST_EXPECT(trustedKeys->trusted (localKey));
|
||||
@@ -770,7 +764,8 @@ private:
|
||||
emptyLocalKey, cfgKeys, cfgPublishers));
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
BEAST_EXPECT(trustedKeys->quorum () ==
|
||||
ValidatorList::calculateQuorum(cfgKeys.size()));
|
||||
(cfgKeys.size() <= 5) ? cfgKeys.size()/2 + 1 :
|
||||
cfgKeys.size() * 2/3 + 1);
|
||||
for (auto const& key : activeValidators)
|
||||
BEAST_EXPECT(trustedKeys->trusted (key));
|
||||
}
|
||||
@@ -799,16 +794,80 @@ private:
|
||||
localKey, cfgKeys, cfgPublishers));
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
|
||||
// When running as an unlisted validator,
|
||||
// the quorum is incremented by 1 for 3 or 5 trusted validators.
|
||||
auto expectedQuorum = ValidatorList::calculateQuorum(cfgKeys.size());
|
||||
if (cfgKeys.size() == 3 || cfgKeys.size() == 5)
|
||||
++expectedQuorum;
|
||||
BEAST_EXPECT(trustedKeys->quorum () == expectedQuorum);
|
||||
BEAST_EXPECT(trustedKeys->quorum () ==
|
||||
(cfgKeys.size() <= 5) ? cfgKeys.size()/2 + 1 :
|
||||
(cfgKeys.size() + 1) * 2/3 + 1);
|
||||
|
||||
for (auto const& key : activeValidators)
|
||||
BEAST_EXPECT(trustedKeys->trusted (key));
|
||||
}
|
||||
}
|
||||
{
|
||||
// Trusted set should be trimmed with multiple validator lists
|
||||
ManifestCache manifests;
|
||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
||||
|
||||
hash_set<PublicKey> activeValidators;
|
||||
|
||||
std::vector<PublicKey> valKeys;
|
||||
valKeys.reserve(20);
|
||||
|
||||
while (valKeys.size () != 20)
|
||||
{
|
||||
valKeys.push_back (randomNode());
|
||||
activeValidators.emplace (valKeys.back());
|
||||
}
|
||||
|
||||
auto addPublishedList = [this, &env, &trustedKeys, &valKeys]()
|
||||
{
|
||||
auto const publisherSecret = randomSecretKey();
|
||||
auto const publisherPublic =
|
||||
derivePublicKey(KeyType::ed25519, publisherSecret);
|
||||
auto const pubSigningKeys = randomKeyPair(KeyType::secp256k1);
|
||||
auto const manifest = beast::detail::base64_encode(makeManifestString (
|
||||
publisherPublic, publisherSecret,
|
||||
pubSigningKeys.first, pubSigningKeys.second, 1));
|
||||
|
||||
std::vector<std::string> cfgPublishers({
|
||||
strHex(publisherPublic)});
|
||||
PublicKey emptyLocalKey;
|
||||
std::vector<std::string> emptyCfgKeys;
|
||||
|
||||
BEAST_EXPECT(trustedKeys->load (
|
||||
emptyLocalKey, emptyCfgKeys, cfgPublishers));
|
||||
|
||||
auto const version = 1;
|
||||
auto const sequence = 1;
|
||||
NetClock::time_point const expiration =
|
||||
env.timeKeeper().now() + 3600s;
|
||||
auto const blob = makeList (
|
||||
valKeys, sequence, expiration.time_since_epoch().count());
|
||||
auto const sig = signList (blob, pubSigningKeys);
|
||||
|
||||
BEAST_EXPECT(ListDisposition::accepted == trustedKeys->applyList (
|
||||
manifest, blob, sig, version));
|
||||
};
|
||||
|
||||
// Apply multiple published lists
|
||||
for (auto i = 0; i < 3; ++i)
|
||||
addPublishedList();
|
||||
|
||||
trustedKeys->onConsensusStart (activeValidators);
|
||||
|
||||
// Minimum quorum should be used
|
||||
BEAST_EXPECT(trustedKeys->quorum () == (valKeys.size() * 2/3 + 1));
|
||||
|
||||
std::size_t nTrusted = 0;
|
||||
for (auto const& key : activeValidators)
|
||||
{
|
||||
if (trustedKeys->trusted (key))
|
||||
++nTrusted;
|
||||
}
|
||||
|
||||
// The number of trusted keys should be 125% of the minimum quorum
|
||||
BEAST_EXPECT(nTrusted == trustedKeys->quorum () * 5 / 4);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -816,7 +875,6 @@ public:
|
||||
run() override
|
||||
{
|
||||
testGenesisQuorum ();
|
||||
testCalculateQuorum ();
|
||||
testConfigLoad ();
|
||||
testApplyList ();
|
||||
testUpdate ();
|
||||
|
||||
Reference in New Issue
Block a user