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:
Mark Travis
2017-08-07 03:00:22 -07:00
committed by Nik Bougalis
parent 35d81e65c1
commit d90a0647d6
5 changed files with 54 additions and 32 deletions

View File

@@ -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;
} }
} }

View File

@@ -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

View File

@@ -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)
{ {
} }

View File

@@ -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;

View File

@@ -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));
} }
} }