mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-22 12:05:53 +00:00
Get quorum and trusted master validator keys from validators.txt:
* Load specified [validators_file] relative to config dir * Add default [validators_file] to rippled-example.cfg * Remove [validators] and [validation_quorum] from rippled-example.cfg * Add [validation_quorum] to validators-example.txt * Allow validators.txt to be a symlink * Throw for invalid [validators_file] instead of logging * Trust own master public key from configured manifest * Do not load untrusted manifests from database Trusted validators are loaded from [validators] and [validator_keys] sections from both rippled.cfg and validators.txt Quorum is loaded from [validation_quorum] section in validators.txt only if it is not configured in rippled.cfg
This commit is contained in:
@@ -513,14 +513,6 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# [validation_quorum]
|
|
||||||
#
|
|
||||||
# Sets the minimum number of trusted validations a ledger must have before
|
|
||||||
# the server considers it fully validated. Note that if you are validating,
|
|
||||||
# your validation counts.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# [ledger_history]
|
# [ledger_history]
|
||||||
#
|
#
|
||||||
# The number of past ledgers to acquire on server startup and the minimum to
|
# The number of past ledgers to acquire on server startup and the minimum to
|
||||||
@@ -562,29 +554,21 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# [validators]
|
|
||||||
#
|
|
||||||
# List of the validation public keys of nodes to always accept as validators.
|
|
||||||
# A comment may, optionally, be associated with each entry, separated by
|
|
||||||
# whitespace from the validation public key.
|
|
||||||
#
|
|
||||||
# Examples:
|
|
||||||
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
|
||||||
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# [validators_file]
|
# [validators_file]
|
||||||
#
|
#
|
||||||
# Path to a file that contains the validation public keys of nodes to always
|
# Path or name of a file that contains the validation public keys of nodes
|
||||||
# accept as validators. A comment may, optionally, be associated with each
|
# to always accept as validators as well as the minimum number of validators
|
||||||
# entry, separated by whitespace from the validation public key.
|
# needed to accept consensus.
|
||||||
#
|
#
|
||||||
# The contents of the file should include a [validators] entry, followed by
|
# The contents of the file should include a [validators] and a
|
||||||
# a list of validation public keys of nodes to always accept as validators,
|
# [validation_quorum] entry. [validators] should be followed by
|
||||||
# one per line, optionally followed by a comment separated by whitespace:
|
# a list of validation public keys of nodes, one per line, optionally
|
||||||
|
# followed by a comment separated by whitespace.
|
||||||
|
# [validation_quorum] should be followed by a number.
|
||||||
#
|
#
|
||||||
# Specify the file by specifying its full path.
|
# Specify the file by its name or path.
|
||||||
|
# Unless an absolute path is specified, it will be considered relative to
|
||||||
|
# the folder in which the rippled.cfg file is located.
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
# /home/ripple/validators.txt
|
# /home/ripple/validators.txt
|
||||||
@@ -598,6 +582,9 @@
|
|||||||
# n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
# n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
||||||
# n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
# n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
||||||
#
|
#
|
||||||
|
# [validation_quorum]
|
||||||
|
# 3
|
||||||
|
#
|
||||||
#
|
#
|
||||||
# [path_search]
|
# [path_search]
|
||||||
# When searching for paths, the default search aggressiveness. This can take
|
# When searching for paths, the default search aggressiveness. This can take
|
||||||
@@ -987,22 +974,11 @@ pool.ntp.org
|
|||||||
[ips]
|
[ips]
|
||||||
r.ripple.com 51235
|
r.ripple.com 51235
|
||||||
|
|
||||||
# Public keys of the validators that this rippled instance trusts. The latest
|
# File containing validation quorum and trusted validator keys.
|
||||||
# list of validators can be obtained from https://ripple.com/ripple.txt
|
# Unless an absolute path is specified, it will be considered relative to the
|
||||||
#
|
# folder in which the rippled.cfg file is located.
|
||||||
# See also https://wiki.ripple.com/Ripple.txt
|
[validators_file]
|
||||||
#
|
validators.txt
|
||||||
[validators]
|
|
||||||
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1
|
|
||||||
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2
|
|
||||||
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3
|
|
||||||
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
|
||||||
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
|
||||||
|
|
||||||
# The number of validators rippled needs to accept a consensus.
|
|
||||||
# Don't change this unless you know what you're doing.
|
|
||||||
[validation_quorum]
|
|
||||||
3
|
|
||||||
|
|
||||||
# Turn down default logging to save disk space in the long run.
|
# Turn down default logging to save disk space in the long run.
|
||||||
# Valid values here are trace, debug, info, warning, error, and fatal
|
# Valid values here are trace, debug, info, warning, error, and fatal
|
||||||
|
|||||||
@@ -8,20 +8,41 @@
|
|||||||
# Blank lines and lines starting with a '#' are ignored.
|
# Blank lines and lines starting with a '#' are ignored.
|
||||||
# All other lines should be hankos or domain names.
|
# All other lines should be hankos or domain names.
|
||||||
#
|
#
|
||||||
# [validators]:
|
|
||||||
# List of nodes to accept as validators specified by public key or domain.
|
|
||||||
#
|
#
|
||||||
# For domains, rippled will probe for https web servers at the specified
|
|
||||||
# domain in the following order: ripple.DOMAIN, www.DOMAIN, DOMAIN
|
|
||||||
#
|
#
|
||||||
# Examples: redstem.com
|
# [validators]
|
||||||
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
#
|
||||||
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
|
# List of the validation public keys of nodes to always accept as validators.
|
||||||
|
# A comment may, optionally, be associated with each entry, separated by
|
||||||
|
# whitespace from the validation public key.
|
||||||
|
#
|
||||||
|
# The latest list of recommended validators can be obtained from
|
||||||
|
# https://ripple.com/ripple.txt
|
||||||
|
#
|
||||||
|
# See also https://wiki.ripple.com/Ripple.txt
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
||||||
|
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# [validation_quorum]
|
||||||
|
#
|
||||||
|
# Sets the minimum number of trusted validations a ledger must have before
|
||||||
|
# the server considers it fully validated. Note that if you are validating,
|
||||||
|
# your validation counts.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Public keys of the validators that this rippled instance trusts.
|
||||||
[validators]
|
[validators]
|
||||||
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1
|
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1
|
||||||
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2
|
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2
|
||||||
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3
|
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3
|
||||||
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
||||||
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
||||||
|
|
||||||
|
# The number of validators rippled needs to accept a consensus.
|
||||||
|
# Don't change this unless you know what you're doing.
|
||||||
|
[validation_quorum]
|
||||||
|
3
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ struct ConfigSection
|
|||||||
#define SECTION_VALIDATION_SEED "validation_seed"
|
#define SECTION_VALIDATION_SEED "validation_seed"
|
||||||
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
|
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
|
||||||
#define SECTION_VALIDATORS "validators"
|
#define SECTION_VALIDATORS "validators"
|
||||||
|
#define SECTION_VALIDATOR_KEYS "validator_keys"
|
||||||
|
#define SECTION_VALIDATION_MANIFEST "validation_manifest"
|
||||||
#define SECTION_VETO_AMENDMENTS "veto_amendments"
|
#define SECTION_VETO_AMENDMENTS "veto_amendments"
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -426,11 +426,13 @@ void Config::loadFromString (std::string const& fileContents)
|
|||||||
if (getSingleSection (secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
|
if (getSingleSection (secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
|
||||||
PATH_SEARCH_MAX = beast::lexicalCastThrow <int> (strTemp);
|
PATH_SEARCH_MAX = beast::lexicalCastThrow <int> (strTemp);
|
||||||
|
|
||||||
// If a file was explicitly specified, then warn if the
|
// If a file was explicitly specified, then throw if the
|
||||||
// path is malformed or if the file does not exist or is
|
// path is malformed or if the file does not exist or is
|
||||||
// not a file.
|
// not a file.
|
||||||
|
// If the specified file is not an absolute path, then look
|
||||||
|
// for it in the same directory as the config file.
|
||||||
// If no path was specified, then look for validators.txt
|
// If no path was specified, then look for validators.txt
|
||||||
// in the same path as the config file but don't complain
|
// in the same directory as the config file, but don't complain
|
||||||
// if we can't find it.
|
// if we can't find it.
|
||||||
boost::filesystem::path validatorsFile;
|
boost::filesystem::path validatorsFile;
|
||||||
|
|
||||||
@@ -439,29 +441,22 @@ void Config::loadFromString (std::string const& fileContents)
|
|||||||
validatorsFile = strTemp;
|
validatorsFile = strTemp;
|
||||||
|
|
||||||
if (validatorsFile.empty ())
|
if (validatorsFile.empty ())
|
||||||
{
|
Throw<std::runtime_error> (
|
||||||
JLOG (j_.error()) <<
|
"Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
|
||||||
"[" SECTION_VALIDATORS_FILE "]" <<
|
|
||||||
": " << strTemp <<
|
if (!validatorsFile.is_absolute())
|
||||||
" is not a valid path";
|
validatorsFile = CONFIG_DIR / validatorsFile;
|
||||||
validatorsFile.clear ();
|
|
||||||
}
|
if (!boost::filesystem::exists (validatorsFile))
|
||||||
else if (!boost::filesystem::exists (validatorsFile))
|
Throw<std::runtime_error> (
|
||||||
{
|
"The file specified in [" SECTION_VALIDATORS_FILE "] "
|
||||||
JLOG (j_.error()) <<
|
"does not exist: " + validatorsFile.string());
|
||||||
"[" SECTION_VALIDATORS_FILE "]" <<
|
|
||||||
": the file " << validatorsFile <<
|
else if (!boost::filesystem::is_regular_file (validatorsFile) &&
|
||||||
" does not exist";
|
!boost::filesystem::is_symlink (validatorsFile))
|
||||||
validatorsFile.clear ();
|
Throw<std::runtime_error> (
|
||||||
}
|
"Invalid file specified in [" SECTION_VALIDATORS_FILE "]: " +
|
||||||
else if (!boost::filesystem::is_regular_file (validatorsFile))
|
validatorsFile.string());
|
||||||
{
|
|
||||||
JLOG (j_.error()) <<
|
|
||||||
"[" SECTION_VALIDATORS_FILE "]" <<
|
|
||||||
": the file " << validatorsFile <<
|
|
||||||
" is not a regular file";
|
|
||||||
validatorsFile.clear ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -471,14 +466,16 @@ void Config::loadFromString (std::string const& fileContents)
|
|||||||
{
|
{
|
||||||
if(!boost::filesystem::exists (validatorsFile))
|
if(!boost::filesystem::exists (validatorsFile))
|
||||||
validatorsFile.clear();
|
validatorsFile.clear();
|
||||||
else if (!boost::filesystem::is_regular_file (validatorsFile))
|
else if (!boost::filesystem::is_regular_file (validatorsFile) &&
|
||||||
|
!boost::filesystem::is_symlink (validatorsFile))
|
||||||
validatorsFile.clear();
|
validatorsFile.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validatorsFile.empty () &&
|
if (!validatorsFile.empty () &&
|
||||||
boost::filesystem::exists (validatorsFile) &&
|
boost::filesystem::exists (validatorsFile) &&
|
||||||
boost::filesystem::is_regular_file (validatorsFile))
|
(boost::filesystem::is_regular_file (validatorsFile) ||
|
||||||
|
boost::filesystem::is_symlink (validatorsFile)))
|
||||||
{
|
{
|
||||||
std::ifstream ifsDefault (validatorsFile.native().c_str());
|
std::ifstream ifsDefault (validatorsFile.native().c_str());
|
||||||
|
|
||||||
@@ -494,17 +491,36 @@ void Config::loadFromString (std::string const& fileContents)
|
|||||||
iniFile,
|
iniFile,
|
||||||
SECTION_VALIDATORS);
|
SECTION_VALIDATORS);
|
||||||
|
|
||||||
if (!entries)
|
if (entries)
|
||||||
{
|
|
||||||
JLOG (j_.error()) <<
|
|
||||||
"[" SECTION_VALIDATORS_FILE "]" <<
|
|
||||||
": the file " << validatorsFile <<
|
|
||||||
" does not contain a [" SECTION_VALIDATORS <<
|
|
||||||
"] section";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
section (SECTION_VALIDATORS).append (*entries);
|
section (SECTION_VALIDATORS).append (*entries);
|
||||||
|
|
||||||
|
auto valKeyEntries = getIniFileSection(
|
||||||
|
iniFile,
|
||||||
|
SECTION_VALIDATOR_KEYS);
|
||||||
|
|
||||||
|
if (valKeyEntries)
|
||||||
|
section (SECTION_VALIDATOR_KEYS).append (*valKeyEntries);
|
||||||
|
|
||||||
|
if (!entries && !valKeyEntries)
|
||||||
|
Throw<std::runtime_error> (
|
||||||
|
"The file specified in [" SECTION_VALIDATORS_FILE "] "
|
||||||
|
"does not contain a [" SECTION_VALIDATORS "] or "
|
||||||
|
"[" SECTION_VALIDATOR_KEYS "] section: " +
|
||||||
|
validatorsFile.string());
|
||||||
|
|
||||||
|
// Look for [validation_quorum] in the validators file
|
||||||
|
// if it was not in the config
|
||||||
|
if (!getIniFileSection (secConfig, SECTION_VALIDATION_QUORUM))
|
||||||
|
{
|
||||||
|
if (!getSingleSection (
|
||||||
|
iniFile, SECTION_VALIDATION_QUORUM, strTemp, j_))
|
||||||
|
Throw<std::runtime_error> (
|
||||||
|
"The file specified in [" SECTION_VALIDATORS_FILE "] "
|
||||||
|
"does not contain a [" SECTION_VALIDATION_QUORUM "] "
|
||||||
|
"section: " + validatorsFile.string());
|
||||||
|
else
|
||||||
|
VALIDATION_QUORUM = std::max (
|
||||||
|
0, beast::lexicalCastThrow <int> (strTemp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
std::string configContents (std::string const& dbPath)
|
std::string configContents (std::string const& dbPath,
|
||||||
|
std::string const& validatorsFile)
|
||||||
{
|
{
|
||||||
static boost::format configContentsTemplate (R"rippleConfig(
|
static boost::format configContentsTemplate (R"rippleConfig(
|
||||||
[server]
|
[server]
|
||||||
@@ -80,6 +81,7 @@ file_size_mb=8
|
|||||||
file_size_mult=2
|
file_size_mult=2
|
||||||
|
|
||||||
%1%
|
%1%
|
||||||
|
|
||||||
%2%
|
%2%
|
||||||
|
|
||||||
# This needs to be an absolute directory reference, not a relative one.
|
# This needs to be an absolute directory reference, not a relative one.
|
||||||
@@ -98,20 +100,6 @@ pool.ntp.org
|
|||||||
[ips]
|
[ips]
|
||||||
r.ripple.com 51235
|
r.ripple.com 51235
|
||||||
|
|
||||||
# The latest validators can be obtained from
|
|
||||||
# https://ripple.com/ripple.txt
|
|
||||||
#
|
|
||||||
[validators]
|
|
||||||
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1
|
|
||||||
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2
|
|
||||||
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3
|
|
||||||
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
|
||||||
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
|
||||||
|
|
||||||
# Ditto.
|
|
||||||
[validation_quorum]
|
|
||||||
3
|
|
||||||
|
|
||||||
# Turn down default logging to save disk space in the long run.
|
# Turn down default logging to save disk space in the long run.
|
||||||
# Valid values here are trace, debug, info, warning, error, and fatal
|
# Valid values here are trace, debug, info, warning, error, and fatal
|
||||||
[rpc_startup]
|
[rpc_startup]
|
||||||
@@ -126,10 +114,12 @@ n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
|||||||
backend=sqlite
|
backend=sqlite
|
||||||
)rippleConfig");
|
)rippleConfig");
|
||||||
|
|
||||||
if (!dbPath.empty ())
|
std::string dbPathSection =
|
||||||
return boost::str (configContentsTemplate % "[database_path]" % dbPath);
|
dbPath.empty () ? "" : "[database_path]\n" + dbPath;
|
||||||
else
|
std::string valFileSection =
|
||||||
return boost::str (configContentsTemplate % "" % "");
|
validatorsFile.empty () ? "" : "[validators_file]\n" + validatorsFile;
|
||||||
|
return boost::str (
|
||||||
|
configContentsTemplate % dbPathSection % valFileSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,28 +128,28 @@ backend=sqlite
|
|||||||
class ConfigGuard
|
class ConfigGuard
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
bool rmSubDir_{false};
|
||||||
|
|
||||||
|
protected:
|
||||||
using path = boost::filesystem::path;
|
using path = boost::filesystem::path;
|
||||||
path subDir_;
|
path subDir_;
|
||||||
path configFile_;
|
beast::unit_test::suite& test_;
|
||||||
path dataDir_;
|
|
||||||
|
|
||||||
bool rmSubDir_{false};
|
auto rmDir (path const& toRm)
|
||||||
bool rmDataDir_{false};
|
{
|
||||||
|
if (is_directory (toRm) && is_empty (toRm))
|
||||||
Config config_;
|
remove (toRm);
|
||||||
|
else
|
||||||
|
test_.log << "Expected " << toRm.string ()
|
||||||
|
<< " to be an empty existing directory.";
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConfigGuard (std::string subDir, std::string const& dbPath)
|
ConfigGuard (beast::unit_test::suite& test, std::string subDir)
|
||||||
: subDir_ (std::move (subDir)), dataDir_ (dbPath)
|
: subDir_ (std::move (subDir))
|
||||||
|
, test_ (test)
|
||||||
{
|
{
|
||||||
using namespace boost::filesystem;
|
using namespace boost::filesystem;
|
||||||
|
|
||||||
if (dbPath.empty ())
|
|
||||||
{
|
|
||||||
dataDir_ = subDir_ / path (Config::databaseDirName);
|
|
||||||
}
|
|
||||||
|
|
||||||
configFile_ = subDir_ / path (Config::configFileName);
|
|
||||||
{
|
{
|
||||||
if (!exists (subDir_))
|
if (!exists (subDir_))
|
||||||
{
|
{
|
||||||
@@ -170,17 +160,60 @@ public:
|
|||||||
rmSubDir_ = false;
|
rmSubDir_ = false;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Cannot run the test someone created a file where we want to
|
// Cannot run the test. Someone created a file where we want to
|
||||||
// put out directory
|
// put our directory
|
||||||
Throw<std::runtime_error> (
|
Throw<std::runtime_error> (
|
||||||
"Cannot create directory: " + subDir_.string ());
|
"Cannot create directory: " + subDir_.string ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
~ConfigGuard ()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using namespace boost::filesystem;
|
||||||
|
|
||||||
|
if (rmSubDir_)
|
||||||
|
rmDir (subDir_);
|
||||||
|
else
|
||||||
|
test_.log << "Skipping rm dir: " << subDir_.string ();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
// if we throw here, just let it die.
|
||||||
|
test_.log << "Error in ~ConfigGuard: " << e.what ();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Write a rippled config file and remove when done.
|
||||||
|
*/
|
||||||
|
class RippledCfgGuard : ConfigGuard
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
path configFile_;
|
||||||
|
path dataDir_;
|
||||||
|
|
||||||
|
bool rmDataDir_{false};
|
||||||
|
|
||||||
|
Config config_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RippledCfgGuard (beast::unit_test::suite& test,
|
||||||
|
std::string subDir, std::string const& dbPath,
|
||||||
|
std::string const& validatorsFile)
|
||||||
|
: ConfigGuard (test, std::move (subDir)), dataDir_ (dbPath)
|
||||||
|
{
|
||||||
|
if (dbPath.empty ())
|
||||||
|
dataDir_ = subDir_ / path (Config::databaseDirName);
|
||||||
|
|
||||||
|
configFile_ = subDir_ / path (Config::configFileName);
|
||||||
|
|
||||||
if (!exists (configFile_))
|
if (!exists (configFile_))
|
||||||
{
|
{
|
||||||
std::ofstream o (configFile_.string ());
|
std::ofstream o (configFile_.string ());
|
||||||
o << configContents (dbPath);
|
o << configContents (dbPath, validatorsFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -202,42 +235,111 @@ public:
|
|||||||
}
|
}
|
||||||
bool configFileExists () const
|
bool configFileExists () const
|
||||||
{
|
{
|
||||||
return boost::filesystem::is_regular_file (configFile_);
|
return boost::filesystem::exists (configFile_);
|
||||||
}
|
}
|
||||||
~ConfigGuard ()
|
~RippledCfgGuard ()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using namespace boost::filesystem;
|
using namespace boost::filesystem;
|
||||||
if (!is_regular_file (configFile_))
|
if (!boost::filesystem::exists (configFile_))
|
||||||
std::cerr << "Expected " << configFile_.string ()
|
test_.log << "Expected " << configFile_.string ()
|
||||||
<< " to be an existing file.\n";
|
<< " to be an existing file.";
|
||||||
else
|
else
|
||||||
remove (configFile_.string ());
|
remove (configFile_.string ());
|
||||||
|
|
||||||
auto rmDir = [](path const& toRm)
|
|
||||||
{
|
|
||||||
if (is_directory (toRm) && is_empty (toRm))
|
|
||||||
remove (toRm);
|
|
||||||
else
|
|
||||||
std::cerr << "Expected " << toRm.string ()
|
|
||||||
<< " to be an empty existing directory.\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
if (rmDataDir_)
|
if (rmDataDir_)
|
||||||
rmDir (dataDir_);
|
rmDir (dataDir_);
|
||||||
else
|
else
|
||||||
std::cerr << "Skipping rm dir: " << dataDir_.string () << "\n";
|
test_.log << "Skipping rm dir: " << dataDir_.string ();
|
||||||
|
|
||||||
if (rmSubDir_)
|
|
||||||
rmDir (subDir_);
|
|
||||||
else
|
|
||||||
std::cerr << "Skipping rm dir: " << subDir_.string () << "\n";
|
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
// if we throw here, just let it die.
|
// if we throw here, just let it die.
|
||||||
std::cerr << "Error in CreateConfigGuard: " << e.what () << "\n";
|
test_.log << "Error in ~RippledCfgGuard: " << e.what ();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string valFileContents (boost::optional<int> const& quorum)
|
||||||
|
{
|
||||||
|
static boost::format configContentsTemplate (R"rippleConfig(
|
||||||
|
[validators]
|
||||||
|
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
|
||||||
|
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
|
||||||
|
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
|
||||||
|
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
|
||||||
|
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
|
||||||
|
|
||||||
|
[validator_keys]
|
||||||
|
nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
|
||||||
|
nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
||||||
|
nHUPDdcdb2Y5DZAJne4c2iabFuAP3F34xZUgYQT2NH7qfkdapgnz
|
||||||
|
|
||||||
|
%1%
|
||||||
|
|
||||||
|
)rippleConfig");
|
||||||
|
|
||||||
|
std::string quorumSection =
|
||||||
|
quorum ? "[validation_quorum]\n" + to_string(*quorum) : "";
|
||||||
|
return boost::str (
|
||||||
|
configContentsTemplate % quorumSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Write a validators.txt file and remove when done.
|
||||||
|
*/
|
||||||
|
class ValidatorsTxtGuard : ConfigGuard
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
path validatorsFile_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ValidatorsTxtGuard (beast::unit_test::suite& test,
|
||||||
|
std::string subDir, std::string const& validatorsFileName,
|
||||||
|
boost::optional<int> const& quorum)
|
||||||
|
: ConfigGuard (test, std::move (subDir))
|
||||||
|
{
|
||||||
|
using namespace boost::filesystem;
|
||||||
|
validatorsFile_ = current_path () / subDir_ / path (
|
||||||
|
validatorsFileName.empty () ? Config::validatorsFileName :
|
||||||
|
validatorsFileName);
|
||||||
|
|
||||||
|
if (!exists (validatorsFile_))
|
||||||
|
{
|
||||||
|
std::ofstream o (validatorsFile_.string ());
|
||||||
|
o << valFileContents (quorum);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Throw<std::runtime_error> (
|
||||||
|
"Refusing to overwrite existing config file: " +
|
||||||
|
validatorsFile_.string ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool validatorsFileExists () const
|
||||||
|
{
|
||||||
|
return boost::filesystem::exists (validatorsFile_);
|
||||||
|
}
|
||||||
|
std::string validatorsFile () const
|
||||||
|
{
|
||||||
|
return validatorsFile_.string ();
|
||||||
|
}
|
||||||
|
~ValidatorsTxtGuard ()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using namespace boost::filesystem;
|
||||||
|
if (!boost::filesystem::exists (validatorsFile_))
|
||||||
|
test_.log << "Expected " << validatorsFile_.string ()
|
||||||
|
<< " to be an existing file.";
|
||||||
|
else
|
||||||
|
remove (validatorsFile_.string ());
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
// if we throw here, just let it die.
|
||||||
|
test_.log << "Error in ~ValidatorsTxtGuard: " << e.what ();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -301,7 +403,7 @@ port_wss_admin
|
|||||||
expect (c.legacy ("database_path") == dataDirAbs.string ());
|
expect (c.legacy ("database_path") == dataDirAbs.string ());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// No db sectcion.
|
// No db section.
|
||||||
// N.B. Config::setup will give database_path a default,
|
// N.B. Config::setup will give database_path a default,
|
||||||
// load will not.
|
// load will not.
|
||||||
Config c;
|
Config c;
|
||||||
@@ -314,7 +416,7 @@ port_wss_admin
|
|||||||
auto const cwd = current_path ();
|
auto const cwd = current_path ();
|
||||||
path const dataDirRel ("test_data_dir");
|
path const dataDirRel ("test_data_dir");
|
||||||
path const dataDirAbs (cwd / path ("test_db") / dataDirRel);
|
path const dataDirAbs (cwd / path ("test_db") / dataDirRel);
|
||||||
detail::ConfigGuard g ("test_db", dataDirAbs.string ());
|
detail::RippledCfgGuard g (*this, "test_db", dataDirAbs.string (), "");
|
||||||
auto& c (g.config ());
|
auto& c (g.config ());
|
||||||
expect (g.dataDirExists ());
|
expect (g.dataDirExists ());
|
||||||
expect (g.configFileExists ());
|
expect (g.configFileExists ());
|
||||||
@@ -324,7 +426,7 @@ port_wss_admin
|
|||||||
{
|
{
|
||||||
// read from file relative path
|
// read from file relative path
|
||||||
std::string const dbPath ("my_db");
|
std::string const dbPath ("my_db");
|
||||||
detail::ConfigGuard g ("test_db", dbPath);
|
detail::RippledCfgGuard g (*this, "test_db", dbPath, "");
|
||||||
auto& c (g.config ());
|
auto& c (g.config ());
|
||||||
std::string const nativeDbPath = absolute (path (dbPath)).string ();
|
std::string const nativeDbPath = absolute (path (dbPath)).string ();
|
||||||
expect (g.dataDirExists ());
|
expect (g.dataDirExists ());
|
||||||
@@ -334,7 +436,7 @@ port_wss_admin
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// read from file no path
|
// read from file no path
|
||||||
detail::ConfigGuard g ("test_db", "");
|
detail::RippledCfgGuard g (*this, "test_db", "", "");
|
||||||
auto& c (g.config ());
|
auto& c (g.config ());
|
||||||
std::string const nativeDbPath =
|
std::string const nativeDbPath =
|
||||||
absolute (path ("test_db") /
|
absolute (path ("test_db") /
|
||||||
@@ -346,10 +448,245 @@ port_wss_admin
|
|||||||
"dbPath No Path");
|
"dbPath No Path");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void testValidatorsFile ()
|
||||||
|
{
|
||||||
|
testcase ("validators_file");
|
||||||
|
|
||||||
|
using namespace boost::filesystem;
|
||||||
|
{
|
||||||
|
// load should throw for missing specified validators file
|
||||||
|
Config c;
|
||||||
|
boost::format cc ("[validators_file]\n%1%\n");
|
||||||
|
std::string error;
|
||||||
|
std::string const missingPath = "/no/way/this/path/exists";
|
||||||
|
auto const expectedError =
|
||||||
|
"The file specified in [validators_file] does not exist: " +
|
||||||
|
missingPath;
|
||||||
|
try {
|
||||||
|
c.loadFromString (boost::str (cc % missingPath));
|
||||||
|
} catch (std::runtime_error& e) {
|
||||||
|
error = e.what();
|
||||||
|
}
|
||||||
|
expect (error == expectedError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load should throw for invalid [validators_file]
|
||||||
|
int const quorum = 3;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.cfg", quorum);
|
||||||
|
Config c;
|
||||||
|
path const invalidFile = current_path () / "test_cfg";
|
||||||
|
boost::format cc ("[validators_file]\n%1%\n");
|
||||||
|
std::string error;
|
||||||
|
auto const expectedError =
|
||||||
|
"Invalid file specified in [validators_file]: " +
|
||||||
|
invalidFile.string ();
|
||||||
|
try {
|
||||||
|
c.loadFromString (boost::str (cc % invalidFile.string ()));
|
||||||
|
} catch (std::runtime_error& e) {
|
||||||
|
error = e.what();
|
||||||
|
}
|
||||||
|
expect (error == expectedError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load validators and quorum from config
|
||||||
|
Config c;
|
||||||
|
std::string toLoad(R"rippleConfig(
|
||||||
|
[validators]
|
||||||
|
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
|
||||||
|
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
|
||||||
|
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
|
||||||
|
|
||||||
|
[validator_keys]
|
||||||
|
nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
|
||||||
|
nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
||||||
|
|
||||||
|
[validation_quorum]
|
||||||
|
4
|
||||||
|
)rippleConfig");
|
||||||
|
c.loadFromString (toLoad);
|
||||||
|
expect (c.legacy ("validators_file").empty ());
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 3);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 2);
|
||||||
|
expect (c.VALIDATION_QUORUM == 4);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load from specified [validators_file] absolute path
|
||||||
|
int const quorum = 3;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.cfg", quorum);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
Config c;
|
||||||
|
boost::format cc ("[validators_file]\n%1%\n");
|
||||||
|
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
||||||
|
expect (c.legacy ("validators_file") == vtg.validatorsFile ());
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 5);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
|
||||||
|
expect (c.VALIDATION_QUORUM == quorum);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load from specified [validators_file] file name
|
||||||
|
// in config directory
|
||||||
|
int const quorum = 3;
|
||||||
|
std::string const valFileName = "validators.txt";
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", valFileName, quorum);
|
||||||
|
detail::RippledCfgGuard rcg (
|
||||||
|
*this, "test_cfg", "", valFileName);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
expect (rcg.configFileExists ());
|
||||||
|
auto& c (rcg.config ());
|
||||||
|
expect (c.legacy ("validators_file") == valFileName);
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 5);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
|
||||||
|
expect (c.VALIDATION_QUORUM == quorum);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load from specified [validators_file] relative path
|
||||||
|
// to config directory
|
||||||
|
int const quorum = 3;
|
||||||
|
std::string const valFilePath = "../test_cfg/validators.txt";
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.txt", quorum);
|
||||||
|
detail::RippledCfgGuard rcg (
|
||||||
|
*this, "test_cfg", "", valFilePath);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
expect (rcg.configFileExists ());
|
||||||
|
auto& c (rcg.config ());
|
||||||
|
expect (c.legacy ("validators_file") == valFilePath);
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 5);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
|
||||||
|
expect (c.VALIDATION_QUORUM == quorum);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load from validators file in default location
|
||||||
|
int const quorum = 3;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.txt", quorum);
|
||||||
|
detail::RippledCfgGuard rcg (*this, "test_cfg", "", "");
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
expect (rcg.configFileExists ());
|
||||||
|
auto& c (rcg.config ());
|
||||||
|
expect (c.legacy ("validators_file").empty ());
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 5);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
|
||||||
|
expect (c.VALIDATION_QUORUM == quorum);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load from specified [validators_file] instead
|
||||||
|
// of default location
|
||||||
|
int const quorum = 3;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.cfg", quorum);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
detail::ValidatorsTxtGuard vtgDefault (
|
||||||
|
*this, "test_cfg", "validators.txt", 4);
|
||||||
|
expect (vtgDefault.validatorsFileExists ());
|
||||||
|
detail::RippledCfgGuard rcg (
|
||||||
|
*this, "test_cfg", "", vtg.validatorsFile ());
|
||||||
|
expect (rcg.configFileExists ());
|
||||||
|
auto& c (rcg.config ());
|
||||||
|
expect (c.legacy ("validators_file") == vtg.validatorsFile ());
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 5);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
|
||||||
|
expect (c.VALIDATION_QUORUM == quorum);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// do not load quorum from validators file if in config
|
||||||
|
boost::format cc (R"rippleConfig(
|
||||||
|
[validators_file]
|
||||||
|
%1%
|
||||||
|
|
||||||
|
[validation_quorum]
|
||||||
|
4
|
||||||
|
)rippleConfig");
|
||||||
|
int const quorum = 3;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.cfg", quorum);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
Config c;
|
||||||
|
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
||||||
|
expect (c.legacy ("validators_file") == vtg.validatorsFile ());
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 5);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
|
||||||
|
expect (c.VALIDATION_QUORUM == 4);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load validators from both config and validators file
|
||||||
|
boost::format cc (R"rippleConfig(
|
||||||
|
[validators_file]
|
||||||
|
%1%
|
||||||
|
|
||||||
|
[validators]
|
||||||
|
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
|
||||||
|
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
|
||||||
|
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
|
||||||
|
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
|
||||||
|
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
|
||||||
|
|
||||||
|
[validator_keys]
|
||||||
|
nHB1X37qrniVugfQcuBTAjswphC1drx7QjFFojJPZwKHHnt8kU7v
|
||||||
|
nHUkAWDR4cB8AgPg7VXMX6et8xRTQb2KJfgv1aBEXozwrawRKgMB
|
||||||
|
|
||||||
|
)rippleConfig");
|
||||||
|
int const quorum = 3;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.cfg", quorum);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
Config c;
|
||||||
|
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
||||||
|
expect (c.legacy ("validators_file") == vtg.validatorsFile ());
|
||||||
|
expect (c.section (SECTION_VALIDATORS).values ().size () == 10);
|
||||||
|
expect (c.section (SECTION_VALIDATOR_KEYS).values ().size () == 5);
|
||||||
|
expect (c.VALIDATION_QUORUM == quorum);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load should throw if [validators] and [validator_keys] are
|
||||||
|
// missing from rippled cfg and validators file
|
||||||
|
Config c;
|
||||||
|
boost::format cc ("[validators_file]\n%1%\n");
|
||||||
|
std::string error;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.cfg", boost::none);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
auto const expectedError =
|
||||||
|
"The file specified in [validators_file] does not contain a "
|
||||||
|
"[validators] or [validator_keys] section: " +
|
||||||
|
vtg.validatorsFile ();
|
||||||
|
std::ofstream o (vtg.validatorsFile ());
|
||||||
|
o << "[validation_quorum]\n3\n";
|
||||||
|
try {
|
||||||
|
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
||||||
|
} catch (std::runtime_error& e) {
|
||||||
|
error = e.what();
|
||||||
|
}
|
||||||
|
expect (error == expectedError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load should throw if [validation_quorum] is
|
||||||
|
// missing from rippled cfg and validators file
|
||||||
|
Config c;
|
||||||
|
boost::format cc ("[validators_file]\n%1%\n");
|
||||||
|
std::string error;
|
||||||
|
detail::ValidatorsTxtGuard vtg (
|
||||||
|
*this, "test_cfg", "validators.cfg", boost::none);
|
||||||
|
expect (vtg.validatorsFileExists ());
|
||||||
|
auto const expectedError =
|
||||||
|
"The file specified in [validators_file] does not contain a "
|
||||||
|
"[validation_quorum] section: " + vtg.validatorsFile ();
|
||||||
|
try {
|
||||||
|
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
||||||
|
} catch (std::runtime_error& e) {
|
||||||
|
error = e.what();
|
||||||
|
}
|
||||||
|
expect (error == expectedError);
|
||||||
|
}
|
||||||
|
}
|
||||||
void run ()
|
void run ()
|
||||||
{
|
{
|
||||||
testLegacy ();
|
testLegacy ();
|
||||||
testDbPath ();
|
testDbPath ();
|
||||||
|
testValidatorsFile ();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -198,6 +198,12 @@ ManifestCache::configManifest (
|
|||||||
Throw<std::runtime_error> ("Unverifiable manifest in config");
|
Throw<std::runtime_error> ("Unverifiable manifest in config");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trust our own master public key
|
||||||
|
if (!trusted(m.masterKey) && !unl.trusted (m.masterKey))
|
||||||
|
{
|
||||||
|
addTrustedKey (m.masterKey, "");
|
||||||
|
}
|
||||||
|
|
||||||
auto const result = applyManifest (std::move(m), unl, journal);
|
auto const result = applyManifest (std::move(m), unl, journal);
|
||||||
|
|
||||||
if (result != ManifestDisposition::accepted)
|
if (result != ManifestDisposition::accepted)
|
||||||
@@ -412,15 +418,15 @@ void ManifestCache::load (
|
|||||||
Throw<std::runtime_error> ("Unverifiable manifest in db");
|
Throw<std::runtime_error> ("Unverifiable manifest in db");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove master public key from permanent trusted key list
|
if (trusted(mo->masterKey) || unl.trusted(mo->masterKey))
|
||||||
if (unl.trusted(mo->masterKey))
|
{
|
||||||
unl.removePermanentKey (mo->masterKey);
|
applyManifest (std::move(*mo), unl, journal);
|
||||||
|
}
|
||||||
// add trusted key
|
else
|
||||||
map_[mo->masterKey];
|
{
|
||||||
|
JLOG(journal.info())
|
||||||
// OK if not accepted (may have been loaded from the config file)
|
<< "Manifest in db is no longer trusted";
|
||||||
applyManifest (std::move(*mo), unl, journal);
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
#include <ripple/app/misc/HashRouter.h>
|
#include <ripple/app/misc/HashRouter.h>
|
||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
|
#include <ripple/core/ConfigSections.h>
|
||||||
#include <ripple/core/DatabaseCon.h>
|
#include <ripple/core/DatabaseCon.h>
|
||||||
#include <ripple/basics/contract.h>
|
#include <ripple/basics/contract.h>
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
@@ -483,15 +484,15 @@ OverlayImpl::setupValidatorKeyManifests (BasicConfig const& config,
|
|||||||
DatabaseCon& db)
|
DatabaseCon& db)
|
||||||
{
|
{
|
||||||
auto const loaded = manifestCache_.loadValidatorKeys (
|
auto const loaded = manifestCache_.loadValidatorKeys (
|
||||||
config.section ("validator_keys"),
|
config.section (SECTION_VALIDATOR_KEYS),
|
||||||
journal_);
|
journal_);
|
||||||
|
|
||||||
if (!loaded)
|
if (!loaded)
|
||||||
Throw<std::runtime_error> (
|
Throw<std::runtime_error> (
|
||||||
"Unable to load keys from [validator_keys]");
|
"Unable to load keys from [" SECTION_VALIDATOR_KEYS "]");
|
||||||
|
|
||||||
auto const validation_manifest =
|
auto const validation_manifest =
|
||||||
config.section ("validation_manifest");
|
config.section (SECTION_VALIDATION_MANIFEST);
|
||||||
|
|
||||||
if (! validation_manifest.lines().empty())
|
if (! validation_manifest.lines().empty())
|
||||||
{
|
{
|
||||||
@@ -513,7 +514,8 @@ OverlayImpl::setupValidatorKeyManifests (BasicConfig const& config,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JLOG(journal_.debug()) << "No [validation_manifest] section in config";
|
JLOG(journal_.debug()) << "No [" SECTION_VALIDATION_MANIFEST <<
|
||||||
|
"] section in config";
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestCache_.load (
|
manifestCache_.load (
|
||||||
|
|||||||
@@ -221,10 +221,19 @@ public:
|
|||||||
expect (!cache.loadValidatorKeys (s6, journal));
|
expect (!cache.loadValidatorKeys (s6, journal));
|
||||||
expect (!cache.trusted (node1));
|
expect (!cache.trusted (node1));
|
||||||
expect (!cache.trusted (node2));
|
expect (!cache.trusted (node2));
|
||||||
|
|
||||||
|
// Trust our own master public key from configured manifest
|
||||||
|
auto unl = std::make_unique<ValidatorList> (journal);
|
||||||
|
|
||||||
|
auto const sk = randomSecretKey();
|
||||||
|
auto const kp = randomKeyPair(KeyType::secp256k1);
|
||||||
|
auto const m = make_Manifest (KeyType::ed25519, sk, kp.first, 0);
|
||||||
|
|
||||||
|
cache.configManifest (clone (m), *unl, journal);
|
||||||
|
expect (cache.trusted (m.masterKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
void testLoadStore (ManifestCache const& m, ValidatorList& unl,
|
void testLoadStore (ManifestCache const& m, ValidatorList& unl)
|
||||||
PublicKey& pk)
|
|
||||||
{
|
{
|
||||||
testcase ("load/store");
|
testcase ("load/store");
|
||||||
|
|
||||||
@@ -236,19 +245,13 @@ public:
|
|||||||
setup.dataDir = getDatabasePath ();
|
setup.dataDir = getDatabasePath ();
|
||||||
DatabaseCon dbCon(setup, dbName, WalletDBInit, WalletDBCount);
|
DatabaseCon dbCon(setup, dbName, WalletDBInit, WalletDBCount);
|
||||||
|
|
||||||
|
if (!m.size ())
|
||||||
|
fail ();
|
||||||
|
|
||||||
m.save (dbCon);
|
m.save (dbCon);
|
||||||
|
|
||||||
ManifestCache loaded;
|
|
||||||
beast::Journal journal;
|
beast::Journal journal;
|
||||||
|
|
||||||
// load should remove master key from permanent key list
|
|
||||||
expect (m.trusted(pk));
|
|
||||||
expect (unl.insertPermanentKey(pk, "trusted key"));
|
|
||||||
expect (unl.trusted(pk));
|
|
||||||
loaded.load (dbCon, unl, journal);
|
|
||||||
expect (!unl.trusted(pk));
|
|
||||||
expect (loaded.trusted(pk));
|
|
||||||
|
|
||||||
auto getPopulatedManifests =
|
auto getPopulatedManifests =
|
||||||
[](ManifestCache const& cache) -> std::vector<Manifest const*>
|
[](ManifestCache const& cache) -> std::vector<Manifest const*>
|
||||||
{
|
{
|
||||||
@@ -270,19 +273,51 @@ public:
|
|||||||
};
|
};
|
||||||
std::vector<Manifest const*> const inManifests (
|
std::vector<Manifest const*> const inManifests (
|
||||||
sort (getPopulatedManifests (m)));
|
sort (getPopulatedManifests (m)));
|
||||||
std::vector<Manifest const*> const loadedManifests (
|
|
||||||
sort (getPopulatedManifests (loaded)));
|
|
||||||
if (inManifests.size () == loadedManifests.size ())
|
|
||||||
{
|
{
|
||||||
expect (std::equal
|
// load should not load untrusted master keys from db
|
||||||
(inManifests.begin (), inManifests.end (),
|
ManifestCache loaded;
|
||||||
loadedManifests.begin (),
|
|
||||||
[](Manifest const* lhs, Manifest const* rhs)
|
loaded.load (dbCon, unl, journal);
|
||||||
{return *lhs == *rhs;}));
|
expect (loaded.size() == 0);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
{
|
||||||
fail ();
|
// load should load all trusted master keys from db
|
||||||
|
ManifestCache loaded;
|
||||||
|
|
||||||
|
for (auto const& man : inManifests)
|
||||||
|
loaded.addTrustedKey (man->masterKey, "");
|
||||||
|
|
||||||
|
loaded.load (dbCon, unl, journal);
|
||||||
|
|
||||||
|
std::vector<Manifest const*> const loadedManifests (
|
||||||
|
sort (getPopulatedManifests (loaded)));
|
||||||
|
|
||||||
|
if (inManifests.size () == loadedManifests.size ())
|
||||||
|
{
|
||||||
|
expect (std::equal
|
||||||
|
(inManifests.begin (), inManifests.end (),
|
||||||
|
loadedManifests.begin (),
|
||||||
|
[](Manifest const* lhs, Manifest const* rhs)
|
||||||
|
{return *lhs == *rhs;}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fail ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// load should remove master key from permanent key list
|
||||||
|
ManifestCache loaded;
|
||||||
|
auto const iMan = inManifests.begin();
|
||||||
|
|
||||||
|
if (!*iMan)
|
||||||
|
fail ();
|
||||||
|
expect (m.trusted((*iMan)->masterKey));
|
||||||
|
expect (unl.insertPermanentKey((*iMan)->masterKey, "trusted key"));
|
||||||
|
expect (unl.trusted((*iMan)->masterKey));
|
||||||
|
loaded.load (dbCon, unl, journal);
|
||||||
|
expect (!unl.trusted((*iMan)->masterKey));
|
||||||
|
expect (loaded.trusted((*iMan)->masterKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boost::filesystem::remove (getDatabasePath () /
|
boost::filesystem::remove (getDatabasePath () /
|
||||||
@@ -315,7 +350,6 @@ public:
|
|||||||
ManifestCache cache;
|
ManifestCache cache;
|
||||||
beast::Journal journal;
|
beast::Journal journal;
|
||||||
auto unl = std::make_unique<ValidatorList> (journal);
|
auto unl = std::make_unique<ValidatorList> (journal);
|
||||||
PublicKey pk;
|
|
||||||
{
|
{
|
||||||
testcase ("apply");
|
testcase ("apply");
|
||||||
auto const accepted = ManifestDisposition::accepted;
|
auto const accepted = ManifestDisposition::accepted;
|
||||||
@@ -360,18 +394,18 @@ public:
|
|||||||
// When trusted permanent key is found as manifest master key
|
// When trusted permanent key is found as manifest master key
|
||||||
// move to manifest cache
|
// move to manifest cache
|
||||||
auto const sk_c = randomSecretKey();
|
auto const sk_c = randomSecretKey();
|
||||||
pk = derivePublicKey(KeyType::ed25519, sk_c);
|
auto const pk_c = derivePublicKey(KeyType::ed25519, sk_c);
|
||||||
auto const kp_c = randomKeyPair(KeyType::secp256k1);
|
auto const kp_c = randomKeyPair(KeyType::secp256k1);
|
||||||
auto const s_c0 = make_Manifest (KeyType::ed25519, sk_c, kp_c.first, 0);
|
auto const s_c0 = make_Manifest (KeyType::ed25519, sk_c, kp_c.first, 0);
|
||||||
expect (unl->insertPermanentKey(pk, "trusted key"));
|
expect (unl->insertPermanentKey(pk_c, "trusted key"));
|
||||||
expect (unl->trusted(pk));
|
expect (unl->trusted(pk_c));
|
||||||
expect (!cache.trusted(pk));
|
expect (!cache.trusted(pk_c));
|
||||||
expect (cache.applyManifest(clone (s_c0), *unl, journal) == accepted);
|
expect (cache.applyManifest(clone (s_c0), *unl, journal) == accepted);
|
||||||
expect (!unl->trusted(pk));
|
expect (!unl->trusted(pk_c));
|
||||||
expect (cache.trusted(pk));
|
expect (cache.trusted(pk_c));
|
||||||
}
|
}
|
||||||
testConfigLoad();
|
testConfigLoad();
|
||||||
testLoadStore (cache, *unl, pk);
|
testLoadStore (cache, *unl);
|
||||||
testGetSignature ();
|
testGetSignature ();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user