mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 03:26:01 +00:00
Change UNL and quorum rules:
* Use fixed size UNL if the total listed validators are below
threshold.
* Set quorum to provide Byzantine fault tolerance until a
threshold of total validators is exceeded, at which time
quorum is 80%.
* Ensure that a quorum of 0 cannot be configured.
This commit is contained in:
committed by
Nik Bougalis
parent
35d81e65c1
commit
d90a0647d6
@@ -49,6 +49,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
||||||
#if defined(BEAST_LINUX) || defined(BEAST_MAC) || defined(BEAST_BSD)
|
#if defined(BEAST_LINUX) || defined(BEAST_MAC) || defined(BEAST_BSD)
|
||||||
@@ -379,12 +380,11 @@ int run (int argc, char** argv)
|
|||||||
vm["rpc_port"].as<std::uint16_t>());
|
vm["rpc_port"].as<std::uint16_t>());
|
||||||
|
|
||||||
if (*config->rpc_port == 0)
|
if (*config->rpc_port == 0)
|
||||||
Throw<std::domain_error> ("");
|
throw std::domain_error("0");
|
||||||
}
|
}
|
||||||
catch(std::exception const&)
|
catch(std::exception const& e)
|
||||||
{
|
{
|
||||||
std::cerr << "Invalid rpc_port = " <<
|
std::cerr << "Invalid rpc_port = " << e.what() << "\n";
|
||||||
vm["rpc_port"].as<std::string>() << std::endl;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -394,11 +394,15 @@ int run (int argc, char** argv)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
config->VALIDATION_QUORUM = vm["quorum"].as <std::size_t> ();
|
config->VALIDATION_QUORUM = vm["quorum"].as <std::size_t> ();
|
||||||
|
if (config->VALIDATION_QUORUM == std::size_t{})
|
||||||
|
{
|
||||||
|
throw std::domain_error("0");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(std::exception const&)
|
catch(std::exception const& e)
|
||||||
{
|
{
|
||||||
std::cerr << "Invalid quorum = " <<
|
std::cerr << "Invalid value specified for --quorum ("
|
||||||
vm["quorum"].as <std::string> () << std::endl;
|
<< e.what() << ")\n";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,15 @@ class ValidatorList
|
|||||||
|
|
||||||
PublicKey localPubKey_;
|
PublicKey localPubKey_;
|
||||||
|
|
||||||
|
// The minimum number of listed validators required to allow removing
|
||||||
|
// non-communicative validators from the trusted set. In other words, if the
|
||||||
|
// number of listed validators is less, then use all of them in the
|
||||||
|
// trusted set.
|
||||||
|
std::size_t const MINIMUM_RESIZEABLE_UNL {25};
|
||||||
|
// The maximum size of a trusted set for which greater than Byzantine fault
|
||||||
|
// tolerance isn't needed.
|
||||||
|
std::size_t const BYZANTINE_THRESHOLD {32};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ValidatorList (
|
ValidatorList (
|
||||||
ManifestCache& validatorManifests,
|
ManifestCache& validatorManifests,
|
||||||
@@ -395,10 +404,13 @@ ValidatorList::onConsensusStart (
|
|||||||
std::pair<std::size_t,PublicKey>(
|
std::pair<std::size_t,PublicKey>(
|
||||||
std::numeric_limits<std::size_t>::max(), localPubKey_));
|
std::numeric_limits<std::size_t>::max(), localPubKey_));
|
||||||
}
|
}
|
||||||
// If no validations are being received, use all validators.
|
// If the total number of validators is too small, or
|
||||||
// Otherwise, do not use validators whose validations aren't being received
|
// no validations are being received, use all validators.
|
||||||
else if (seenValidators.empty() ||
|
// Otherwise, do not use validators whose validations aren't
|
||||||
seenValidators.find (val->first) != seenValidators.end ())
|
// being received.
|
||||||
|
else if (keyListings_.size() < MINIMUM_RESIZEABLE_UNL ||
|
||||||
|
seenValidators.empty() ||
|
||||||
|
seenValidators.find (val->first) != seenValidators.end ())
|
||||||
{
|
{
|
||||||
rankedKeys.insert (
|
rankedKeys.insert (
|
||||||
std::pair<std::size_t,PublicKey>(val->second, val->first));
|
std::pair<std::size_t,PublicKey>(val->second, val->first));
|
||||||
@@ -416,11 +428,12 @@ ValidatorList::onConsensusStart (
|
|||||||
|
|
||||||
auto size = rankedKeys.size();
|
auto size = rankedKeys.size();
|
||||||
|
|
||||||
// Do not require 80% quorum for less than 10 trusted validators
|
// Require 80% quorum if there are lots of validators.
|
||||||
if (rankedKeys.size() >= 10)
|
if (rankedKeys.size() > BYZANTINE_THRESHOLD)
|
||||||
{
|
{
|
||||||
// Use all eligible keys if there is only one trusted list
|
// Use all eligible keys if there is only one trusted list
|
||||||
if (publisherLists_.size() == 1)
|
if (publisherLists_.size() == 1 ||
|
||||||
|
keyListings_.size() < MINIMUM_RESIZEABLE_UNL)
|
||||||
{
|
{
|
||||||
// Try to raise the quorum to at least 80% of the trusted set
|
// Try to raise the quorum to at least 80% of the trusted set
|
||||||
quorum = std::max(quorum, size - size / 5);
|
quorum = std::max(quorum, size - size / 5);
|
||||||
@@ -433,13 +446,13 @@ ValidatorList::onConsensusStart (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minimumQuorum_ && (seenValidators.empty() ||
|
if (minimumQuorum_ && seenValidators.size() < quorum)
|
||||||
rankedKeys.size() < quorum))
|
|
||||||
{
|
{
|
||||||
quorum = *minimumQuorum_;
|
quorum = *minimumQuorum_;
|
||||||
JLOG (j_.warn()) <<
|
JLOG (j_.warn())
|
||||||
"Using unsafe quorum of " << quorum_ <<
|
<< "Using unsafe quorum of "
|
||||||
" as specified in the command line";
|
<< quorum_
|
||||||
|
<< " as specified in the command line";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not use achievable quorum until lists from all configured
|
// Do not use achievable quorum until lists from all configured
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ ValidatorList::ValidatorList (
|
|||||||
, publisherManifests_ (publisherManifests)
|
, publisherManifests_ (publisherManifests)
|
||||||
, timeKeeper_ (timeKeeper)
|
, timeKeeper_ (timeKeeper)
|
||||||
, j_ (j)
|
, j_ (j)
|
||||||
, quorum_ (minimumQuorum ? *minimumQuorum : 1) // Genesis ledger quorum
|
, quorum_ (minimumQuorum.value_or(1)) // Genesis ledger quorum
|
||||||
, minimumQuorum_ (minimumQuorum)
|
, minimumQuorum_ (minimumQuorum)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public:
|
|||||||
int PATH_SEARCH_MAX = 10;
|
int PATH_SEARCH_MAX = 10;
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
boost::optional<std::size_t> VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative
|
boost::optional<std::size_t> VALIDATION_QUORUM; // validations to consider ledger authoritative
|
||||||
|
|
||||||
std::uint64_t FEE_DEFAULT = 10;
|
std::uint64_t FEE_DEFAULT = 10;
|
||||||
std::uint64_t FEE_ACCOUNT_RESERVE = 200*SYSTEM_CURRENCY_PARTS;
|
std::uint64_t FEE_ACCOUNT_RESERVE = 200*SYSTEM_CURRENCY_PARTS;
|
||||||
|
|||||||
@@ -488,16 +488,19 @@ private:
|
|||||||
std::vector<std::string> cfgPublishers;
|
std::vector<std::string> cfgPublishers;
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<PublicKey> activeValidators;
|
||||||
|
|
||||||
|
// BFT: n >= 3f+1
|
||||||
|
std::size_t const n = 40;
|
||||||
|
std::size_t const f = 13;
|
||||||
{
|
{
|
||||||
std::vector<std::string> cfgKeys;
|
std::vector<std::string> cfgKeys;
|
||||||
cfgKeys.reserve(20);
|
cfgKeys.reserve(n);
|
||||||
|
|
||||||
while (cfgKeys.size () != 20)
|
while (cfgKeys.size () != n)
|
||||||
{
|
{
|
||||||
auto const valKey = randomNode();
|
auto const valKey = randomNode();
|
||||||
cfgKeys.push_back (toBase58(
|
cfgKeys.push_back (toBase58(
|
||||||
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
||||||
if (cfgKeys.size () <= 15)
|
if (cfgKeys.size () <= n - 5)
|
||||||
activeValidators.emplace (valKey);
|
activeValidators.emplace (valKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +510,8 @@ private:
|
|||||||
// onConsensusStart should make all available configured
|
// onConsensusStart should make all available configured
|
||||||
// validators trusted
|
// validators trusted
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
trustedKeys->onConsensusStart (activeValidators);
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 14);
|
// Add 1 to n because I'm not on a published list.
|
||||||
|
BEAST_EXPECT(trustedKeys->quorum () == n + 1 - f);
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
for (auto const& val : cfgKeys)
|
for (auto const& val : cfgKeys)
|
||||||
{
|
{
|
||||||
@@ -566,7 +570,7 @@ private:
|
|||||||
manifests.applyManifest(std::move (*m1)) ==
|
manifests.applyManifest(std::move (*m1)) ==
|
||||||
ManifestDisposition::accepted);
|
ManifestDisposition::accepted);
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
trustedKeys->onConsensusStart (activeValidators);
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 15);
|
BEAST_EXPECT(trustedKeys->quorum () == n + 2 - f);
|
||||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->listed (signingPublic1));
|
BEAST_EXPECT(trustedKeys->listed (signingPublic1));
|
||||||
@@ -583,7 +587,7 @@ private:
|
|||||||
manifests.applyManifest(std::move (*m2)) ==
|
manifests.applyManifest(std::move (*m2)) ==
|
||||||
ManifestDisposition::accepted);
|
ManifestDisposition::accepted);
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
trustedKeys->onConsensusStart (activeValidators);
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 15);
|
BEAST_EXPECT(trustedKeys->quorum () == n + 2 - f);
|
||||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->listed (signingPublic2));
|
BEAST_EXPECT(trustedKeys->listed (signingPublic2));
|
||||||
@@ -607,7 +611,7 @@ private:
|
|||||||
BEAST_EXPECT(manifests.getSigningKey (masterPublic) == masterPublic);
|
BEAST_EXPECT(manifests.getSigningKey (masterPublic) == masterPublic);
|
||||||
BEAST_EXPECT(manifests.revoked (masterPublic));
|
BEAST_EXPECT(manifests.revoked (masterPublic));
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
trustedKeys->onConsensusStart (activeValidators);
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 15);
|
BEAST_EXPECT(trustedKeys->quorum () == n + 1 - f);
|
||||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||||
BEAST_EXPECT(!trustedKeys->trusted (masterPublic));
|
BEAST_EXPECT(!trustedKeys->trusted (masterPublic));
|
||||||
BEAST_EXPECT(!trustedKeys->listed (signingPublicMax));
|
BEAST_EXPECT(!trustedKeys->listed (signingPublicMax));
|
||||||
@@ -658,7 +662,7 @@ private:
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Should use custom minimum quorum
|
// Should use custom minimum quorum
|
||||||
std::size_t const minQuorum = 0;
|
std::size_t const minQuorum = 1;
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), beast::Journal (), minQuorum);
|
manifests, manifests, env.timeKeeper(), beast::Journal (), minQuorum);
|
||||||
@@ -811,9 +815,9 @@ private:
|
|||||||
hash_set<PublicKey> activeValidators;
|
hash_set<PublicKey> activeValidators;
|
||||||
|
|
||||||
std::vector<PublicKey> valKeys;
|
std::vector<PublicKey> valKeys;
|
||||||
valKeys.reserve(20);
|
valKeys.reserve(n);
|
||||||
|
|
||||||
while (valKeys.size () != 20)
|
while (valKeys.size () != n)
|
||||||
{
|
{
|
||||||
valKeys.push_back (randomNode());
|
valKeys.push_back (randomNode());
|
||||||
activeValidators.emplace (valKeys.back());
|
activeValidators.emplace (valKeys.back());
|
||||||
@@ -866,7 +870,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The number of trusted keys should be 125% of the minimum quorum
|
// The number of trusted keys should be 125% of the minimum quorum
|
||||||
BEAST_EXPECT(nTrusted == trustedKeys->quorum () * 5 / 4);
|
BEAST_EXPECT(nTrusted ==
|
||||||
|
static_cast<std::size_t>(trustedKeys->quorum () * 5 / 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user