Add validator token to config (RIPD-1386)

This commit is contained in:
wilsonianb
2017-01-18 15:01:50 -08:00
committed by seelabs
parent 2fcde0e0b6
commit a8cf5e0a5c
29 changed files with 372 additions and 191 deletions

View File

@@ -2,7 +2,7 @@
from __future__ import print_function
import base64, os, random, struct, sys
import base64, binascii, json, os, random, struct, sys
import ed25519
import ecdsa
import hashlib
@@ -188,9 +188,14 @@ def perform_check(s, print=print):
def perform_sign(
seq, validation_pk_human, validation_sk_human, master_sk_human, print=print):
print('[validation_manifest]')
print(wrap(get_signature(
int(seq), validation_pk_human, validation_sk_human, master_sk_human)))
manifest = get_signature(
int(seq), validation_pk_human, validation_sk_human, master_sk_human)
print('[validator_token]')
print(wrap(base64.b64encode(json.dumps({
"validation_secret_key": binascii.b2a_hex(Base58.decode_version(validation_sk_human)[1]),
"manifest": manifest},
separators=(',', ':')))))
def perform_verify(
seq, validation_pk_human, master_pk_human, signature, print=print):

View File

@@ -104,9 +104,7 @@ n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
[validation_seed]
{validation_seed}
#vaidation_public_key: {validation_public_key}
#validation_public_key: {validation_public_key}
# Other rippled's trusting this validator need this key
[validator_keys]
@@ -119,8 +117,8 @@ n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
expire = 1
auto_connect = 1
[validation_manifest]
{validation_manifest}
[validator_token]
{validator_token}
[rpc_startup]
{{ "command": "log_level", "severity": "debug" }}
@@ -201,7 +199,7 @@ def sign_manifest(seq, validation_pk, master_secret):
result = []
for l in r.splitlines():
l.strip()
if not l or l == '[validation_manifest]':
if not l or l == '[validator_token]':
continue
result.append(l)
return '\n'.join(result)
@@ -430,7 +428,7 @@ def new_config_ephemeral_key(
if rm_dbs and os.path.exists(db_dir):
shutil.rmtree(db_dir)
os.makedirs(db_dir)
# replace the validation_manifest section with `signed`
# replace the validator_token section with `signed`
bak = config_file + '.bak'
if is_windows() and os.path.isfile(bak):
os.remove(bak)
@@ -440,7 +438,7 @@ def new_config_ephemeral_key(
with open(config_file, 'w') as out:
for l in src:
sl = l.strip()
if not in_manifest and sl == '[validation_manifest]':
if not in_manifest and sl == '[validator_token]':
in_manifest = True
elif in_manifest:
if sl.startswith('[') or sl.startswith('#'):
@@ -539,7 +537,7 @@ def get_configs(manifest_seq):
sibling_ip = '127.0.0.1'
sibling_port = port_nums[sibling_index][1]
d = {
'validation_manifest': s,
'validator_token': s,
'all_validator_keys': all_validator_keys,
'node_db_type': node_db_type,
'node_db_path': node_db_path,

View File

@@ -141,9 +141,13 @@ class test_Sign(TestCase):
Sign.perform_sign(self.SEQUENCE, public, private, private, print=self.print)
self.assertEquals(
self.results,
[[['[validation_manifest]'], {}],
[['JAAAABdxIe2DIKUZd9jDjKikknxnDfWCHkSXYZReFenvsmoVCdIw6nMhAnZ2dnZ2dnZ2dnZ2\n'
'dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dkYwRAIgXyobHA8sDQxmDJNLE6HIaARlzvcd79/wT068\n'
'e113gUkCIHkI540JQT2LHwAD7/y3wFE5X3lEXMfgZRkpLZTxkpticBJAzo5VrUEr0U47sHvu\n'
'IjbrINLCTM6pAScA899G9kpMWexbXv1ceIbTP5JH1HyQmsZsROTeHR0irojvYgx7JLaiAA=='],
[[['[validator_token]'], {}],
[['eyJ2YWxpZGF0aW9uX3ByaXZhdGVfa2V5IjoiNmI2YjZiNmI2YjZiNmI2YjZiNmI2Yj\n'
'ZiNmI2YjZiNmI2YjZiNmI2YjZiNmI2YjZiNmI2YjZiNmI2YjZiNmI2YiIsIm1hbmlm\n'
'ZXN0IjoiSkFBQUFCZHhJZTJESUtVWmQ5akRqS2lra254bkRmV0NIa1NYWVpSZUZlbn\n'
'ZzbW9WQ2RJdzZuTWhBbloyZG5aMmRuWjJkbloyZG5aMmRuWjJkbloyZG5aMmRuWjJk\n'
'bloyZG5aMmRrWXdSQUlnWHlvYkhBOHNEUXhtREpOTEU2SElhQVJsenZjZDc5L3dUMD\n'
'Y4ZTExM2dVa0NJSGtJNTQwSlFUMkxId0FENy95M3dGRTVYM2xFWE1mZ1pSa3BMWlR4\n'
'a3B0aWNCSkF6bzVWclVFcjBVNDdzSHZ1SWpicklOTENUTTZwQVNjQTg5OUc5a3BNV2\n'
'V4Ylh2MWNlSWJUUDVKSDFIeVFtc1pzUk9UZUhSMGlyb2p2WWd4N0pMYWlBQT09In0='],
{}]])

View File

@@ -8,7 +8,7 @@ Validators use two types of key pairs: *master keys* and *ephemeral
keys*. Ephemeral keys are used to sign and verify validations. Master keys are
used to sign and verify manifests that change ephemeral keys. The master secret
key should be tightly controlled. The ephemeral secret key needs to be present
in the config file.
in the config file in the form of a token.
## Validator Keys
@@ -28,10 +28,9 @@ Sample output:
paamfhAn5m1NM4UUu5mmvVaHQy8Fb65bkpbaNrvKwX3YMKdjzi2
```
The first value is the master public key. Add the public key to the config
for this validator. A one-word comment must be added after the key (for example
*ThisServersName*). Any other rippled trusting the validator needs to add the
master public key to its config. Only add keys received from trusted sources.
The first value is the master public key. Any other rippled trusting the
validator needs to add the master public key to its config. Only add keys
received from trusted sources.
The second value is the corresponding master secret key. **DO NOT INSTALL THIS
IN THE CONFIG**. The master secret key will be used to sign manifests that
@@ -63,17 +62,6 @@ Sample output:
}
```
Add the `validation_seed` value (the ephemeral secret key) to this validator's
config. It is recommended to add the ephemeral public key and the sequence
number as a comment as well (sequence numbers are be explained below):
```
[validation_seed]
sh8bLqqkGBknGcsgRTrFMxcciwytm
# validation_public_key: n9LtZ9haqYMbzJ92cDd3pu3Lko6uEznrXuYea3ehuhVcwDHF5coX
# sequence number: 1
```
A manifest is a signed message used to inform other servers of this validator's
ephemeral public key. A manifest contains a sequence number, the new ephemeral
public key, and it is signed with both the ephemeral and master secret keys.
@@ -95,15 +83,18 @@ For example:
Sample output:
```
[validation_manifest]
JAAAAAFxIe3t8rIb4Ba8JHI97CbwpxmTq0LhN/7ZAbsNaSwrbHaypHMhAzTuu07YGOvVvB3+
aS0jhP+q0TVgTjGJKhx+yTY1Da3ddkYwRAIgDkmIt3dPNsfeCH3ApMZgpwqG4JwtIlKEymqK
S7v+VqkCIFQXg20ZMpXXT86vmLdlmPspgeUN1scWsuFoPYUUJywycBJAl93+/bZbfZ4quTeM
5y80/OSIcVoWPcHajwrAl68eiAW4MVFeJXvShXNfnT+XsxMjDh0VpOkhvyp971i1MgjBAA==
```
[validator_token]
eyJ2YWxpZGF0aW9uX3ByaXZhdGVfa2V5IjoiN2VkMWM2ZDVmMTBhOGUyYWNmZWE0OW
NkNzE5ZjhiZjZhMWI1Yjg2M2Q3N2QxMDE1MjVkNGQxOWU4ZWYwNjU1OSIsIm1hbmlm
ZXN0IjoiSkFBQUFBRnhJZTNVZ3lXb3QwdFpSTFBNbDQ1dit5YkdPanJYNkE5c1JIcU
lJWUlZSTZjREhuTWhBNlNqWWZnWnJsTHFXU2UrNzlWUkZnOWJEeVZKNDMvYmV6Zkxu
dWkxVzhQdWRrWXdSQUlnY09KWmVpdmFaOW1abmM4Y1N4bUlOVGVYSUlKOE5oTXprRD
lrWThseGRkc0NJRk1OU3k5Qmo4VHNQWlV4NzNwYUJ0RmxpaDZWOTlSRXNnU3V6Wlhr
BXMHNWTVZXR09QUnY0Ylg2dDVmYi9haUtoNkZyRVdtYk5YeXpRNmI5TGhDQT09In0=
```
Copy this to the config for this validator. Don't forget to update the comment
noting the sequence number.
Copy this to the config for this validator. It is recommended to add the
sequence number as a comment as well.
## Revoking a key

View File

@@ -600,6 +600,15 @@
#
#
#
# [validator_token]
#
# This is an alternative to [validation_seed] that allows rippled to perform
# validation without having to store the validator keys on the network
# connected server. The field should contain a base64-encoded blob.
# External tools are available for generating validator keys and tokens.
#
#
#
# [validators_file]
#
# Path or name of a file that contains the validation public keys of nodes
@@ -634,6 +643,7 @@
# n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
#
#
#
# [path_search]
# When searching for paths, the default search aggressiveness. This can take
# exponentially more resources as the size is increased.

View File

@@ -62,6 +62,11 @@ public:
virtual bool peerPosition (Pos_t const& position) = 0;
virtual PublicKey const& getValidationPublicKey () const = 0;
virtual void setValidationKeys (
SecretKey const& valSecret, PublicKey const& valPublic) = 0;
virtual void startRound (
LgrID_t const& prevLCLHash,
std::shared_ptr<Ledger const> const& prevLedger,

View File

@@ -73,8 +73,6 @@ LedgerConsensusImp<Traits>::LedgerConsensusImp (
, ourID_ (calcNodeID (app.nodeIdentity().first))
, state_ (State::open)
, closeTime_ {}
, valPublic_ (app_.config().VALIDATION_PUB)
, valSecret_ (app_.config().VALIDATION_PRIV)
, consensusFail_ (false)
, roundTime_ (0)
, closePercent_ (0)
@@ -1780,6 +1778,20 @@ void LedgerConsensusImp<Traits>::addLoad(STValidation::ref val)
val->setFieldU32(sfLoadFee, fee);
}
template <class Traits>
PublicKey const& LedgerConsensusImp<Traits>::getValidationPublicKey () const
{
return valPublic_;
}
template <class Traits>
void LedgerConsensusImp<Traits>::setValidationKeys (
SecretKey const& valSecret, PublicKey const& valPublic)
{
valSecret_ = valSecret;
valPublic_ = valPublic;
}
//------------------------------------------------------------------------------
std::shared_ptr <LedgerConsensus<RCLCxTraits>>
make_LedgerConsensus (

View File

@@ -354,6 +354,16 @@ private:
// nodes that have bowed out of this consensus process
hash_set<NodeID_t> deadNodes_;
beast::Journal j_;
public:
/** Returns validation public key */
PublicKey const&
getValidationPublicKey () const override;
/** Set validation private and public key pair. */
void
setValidationKeys (
SecretKey const& valSecret, PublicKey const& valPublic) override;
};
//------------------------------------------------------------------------------

View File

@@ -66,6 +66,7 @@
#include <ripple/overlay/Cluster.h>
#include <ripple/overlay/make_Overlay.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <ripple/protocol/STParsedJSON.h>
#include <ripple/protocol/types.h>
@@ -1102,26 +1103,59 @@ bool ApplicationImp::setup()
return false;
}
if (!validatorManifests_->load (
getWalletDB (), "ValidatorManifests",
config().section (SECTION_VALIDATION_MANIFEST).lines()))
{
JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
return false;
}
PublicKey valPublic;
SecretKey valSecret;
std::string manifest;
if (config().exists (SECTION_VALIDATOR_TOKEN))
{
if (auto const token = ValidatorToken::make_ValidatorToken (
config().section (SECTION_VALIDATOR_TOKEN).lines ()))
{
valSecret = token->validationSecret;
valPublic = derivePublicKey (KeyType::secp256k1, valSecret);
manifest = std::move(token->manifest);
}
else
{
JLOG(m_journal.fatal()) <<
"Invalid entry in validator token configuration.";
return false;
}
}
else if (config().exists (SECTION_VALIDATION_SEED))
{
auto const seed = parseBase58<Seed>(
config().section (SECTION_VALIDATION_SEED).lines ().front());
if (!seed)
Throw<std::runtime_error> (
"Invalid seed specified in [" SECTION_VALIDATION_SEED "]");
valSecret = generateSecretKey (KeyType::secp256k1, *seed);
valPublic = derivePublicKey (KeyType::secp256k1, valSecret);
}
publisherManifests_->load (
getWalletDB (), "PublisherManifests");
if (!validatorManifests_->load (
getWalletDB (), "ValidatorManifests", manifest))
{
JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
return false;
}
// Setup trusted validators
if (!validators_->load (
config_->VALIDATION_PUB,
config().section (SECTION_VALIDATORS).values (),
config().section (SECTION_VALIDATOR_LIST_KEYS).values ()))
{
JLOG(m_journal.fatal()) <<
"Invalid entry in validator configuration.";
return false;
publisherManifests_->load (
getWalletDB (), "PublisherManifests");
m_networkOPs->setValidationKeys (valSecret, valPublic);
// Setup trusted validators
if (!validators_->load (
valPublic,
config().section (SECTION_VALIDATORS).values (),
config().section (SECTION_VALIDATOR_LIST_KEYS).values ()))
{
JLOG(m_journal.fatal()) <<
"Invalid entry in validator configuration.";
return false;
}
}
if (!validatorSites_->load (

View File

@@ -57,6 +57,8 @@ class OrderBookDB;
class Overlay;
class PathRequests;
class PendingSaves;
class PublicKey;
class SecretKey;
class AccountIDCache;
class STLedgerEntry;
class TimeKeeper;

View File

@@ -23,6 +23,7 @@
#include <ripple/app/main/NodeIdentity.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/core/ConfigSections.h>
#include <boost/format.hpp>
#include <boost/optional.hpp>
@@ -32,14 +33,14 @@ std::pair<PublicKey, SecretKey>
loadNodeIdentity (Application& app)
{
// If a seed is specified in the configuration file use that directly.
if (!app.config().NODE_SEED.empty ())
if (app.config().exists(SECTION_NODE_SEED))
{
auto const seed = parseBase58<Seed>(
app.config().NODE_SEED);
app.config().section(SECTION_NODE_SEED).lines().front());
if (!seed)
Throw<std::runtime_error>(
"NodeIdentity: Bad [node_seed] specified");
"NodeIdentity: Bad [" SECTION_NODE_SEED "] specified");
auto secretKey =
generateSecretKey (KeyType::secp256k1, *seed);

View File

@@ -22,6 +22,7 @@
#include <ripple/basics/UnorderedContainers.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <ripple/beast/utility/Journal.h>
#include <boost/optional.hpp>
#include <string>
@@ -54,8 +55,8 @@ namespace ripple {
There are two stores of information within rippled related to manifests.
An instance of ManifestCache stores, for each trusted validator, (a) its
master public key, and (b) the most senior of all valid manifests it has
seen for that validator, if any. On startup, the [validation_manifest]
config entry (which is the manifest for this validator) is decoded and
seen for that validator, if any. On startup, the [validator_token] config
entry (which contains the manifest for this validator) is decoded and
added to the manifest cache. Other manifests are added as "gossip" is
received from rippled peers.
@@ -79,8 +80,6 @@ namespace ripple {
struct Manifest
{
static std::size_t constexpr textLength = 288;
std::string serialized;
PublicKey masterKey;
PublicKey signingKey;
@@ -128,6 +127,22 @@ struct Manifest
Blob getMasterSignature () const;
};
struct ValidatorToken
{
std::string manifest;
SecretKey validationSecret;
private:
ValidatorToken(std::string const& m, SecretKey const& valSecret);
public:
ValidatorToken(ValidatorToken const&) = delete;
ValidatorToken(ValidatorToken&& other) = default;
static boost::optional<ValidatorToken>
make_ValidatorToken(std::vector<std::string> const& tokenBlob);
};
enum class ManifestDisposition
{
/// Manifest is valid
@@ -230,7 +245,7 @@ public:
*/
bool load (
DatabaseCon& dbCon, std::string const& dbTable,
std::vector<std::string> const& configManifest);
std::string const& configManifest);
/** Populate manifest cache with manifests in database.

View File

@@ -354,6 +354,15 @@ public:
{
mConsensus->setLastCloseTime(t);
}
PublicKey const& getValidationPublicKey () const override
{
return mLedgerConsensus->getValidationPublicKey ();
}
void setValidationKeys (
SecretKey const& valSecret, PublicKey const& valPublic) override
{
mLedgerConsensus->setValidationKeys (valSecret, valPublic);
}
Json::Value getConsensusInfo () override;
Json::Value getServerInfo (bool human, bool admin) override;
void clearLedgerFetch () override;
@@ -2043,35 +2052,11 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
if (admin)
{
if (app_.config().VALIDATION_PUB.size ())
if (getValidationPublicKey().size ())
{
auto const& validation_manifest =
app_.config().section (SECTION_VALIDATION_MANIFEST);
if (! validation_manifest.lines().empty())
{
std::string s;
s.reserve (Manifest::textLength);
for (auto const& line : validation_manifest.lines())
s += beast::rfc2616::trim(line);
if (auto mo = Manifest::make_Manifest (beast::detail::base64_decode(s)))
{
Json::Value valManifest = Json::objectValue;
valManifest [jss::master_key] = toBase58 (
TokenType::TOKEN_NODE_PUBLIC,
mo->masterKey);
valManifest [jss::signing_key] = toBase58 (
TokenType::TOKEN_NODE_PUBLIC,
mo->signingKey);
valManifest [jss::seq] = Json::UInt (mo->sequence);
info[jss::validation_manifest] = valManifest;
}
}
info[jss::pubkey_validator] = toBase58 (
TokenType::TOKEN_NODE_PUBLIC,
app_.config().VALIDATION_PUB);
app_.validators().localPublicKey());
}
else
{

View File

@@ -177,6 +177,9 @@ public:
// FIXME(NIKB): Remove the need for this function
virtual void setLastCloseTime (NetClock::time_point t) = 0;
virtual PublicKey const& getValidationPublicKey () const = 0;
virtual void setValidationKeys (
SecretKey const& valSecret, PublicKey const& valPublic) = 0;
virtual Json::Value getConsensusInfo () = 0;
virtual Json::Value getServerInfo (bool human, bool admin) = 0;

View File

@@ -280,6 +280,15 @@ public:
trustedPublisher (
PublicKey const& identity) const;
/** Returns local validator public key
@par Thread Safety
May be called concurrently
*/
PublicKey
localPublicKey () const;
/** Invokes the callback once for every listed validation public key.
@note Undefined behavior results when calling ValidatorList members from

View File

@@ -20,11 +20,15 @@
#include <ripple/app/misc/Manifest.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/beast/rfc2616.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/json/json_reader.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/Sign.h>
#include <beast/core/detail/base64.hpp>
#include <boost/regex.hpp>
#include <numeric>
#include <stdexcept>
namespace ripple {
@@ -139,6 +143,58 @@ Blob Manifest::getMasterSignature () const
return st.getFieldVL (sfMasterSignature);
}
ValidatorToken::ValidatorToken(std::string const& m, SecretKey const& valSecret)
: manifest(m)
, validationSecret(valSecret)
{
}
boost::optional<ValidatorToken>
ValidatorToken::make_ValidatorToken(std::vector<std::string> const& tokenBlob)
{
try
{
std::string tokenStr;
tokenStr.reserve (
std::accumulate (tokenBlob.cbegin(), tokenBlob.cend(), std::size_t(0),
[] (std::size_t init, std::string const& s)
{
return init + s.size();
}));
for (auto const& line : tokenBlob)
tokenStr += beast::rfc2616::trim(line);
tokenStr = beast::detail::base64_decode(tokenStr);
Json::Reader r;
Json::Value token;
if (! r.parse (tokenStr, token))
return boost::none;
if (token.isMember("manifest") && token["manifest"].isString() &&
token.isMember("validation_secret_key") &&
token["validation_secret_key"].isString())
{
auto const ret = strUnHex (token["validation_secret_key"].asString());
if (! ret.second || ! ret.first.size ())
return boost::none;
return ValidatorToken(
token["manifest"].asString(),
SecretKey(Slice{ret.first.data(), ret.first.size()}));
}
else
{
return boost::none;
}
}
catch (std::exception const&)
{
return boost::none;
}
}
PublicKey
ManifestCache::getSigningKey (PublicKey const& pk) const
{
@@ -303,18 +359,14 @@ ManifestCache::load (
bool
ManifestCache::load (
DatabaseCon& dbCon, std::string const& dbTable,
std::vector<std::string> const& configManifest)
std::string const& configManifest)
{
load (dbCon, dbTable);
if (! configManifest.empty())
{
std::string s;
s.reserve (Manifest::textLength);
for (auto const& line : configManifest)
s += beast::rfc2616::trim(line);
auto mo = Manifest::make_Manifest (beast::detail::base64_decode(s));
auto mo = Manifest::make_Manifest (
beast::detail::base64_decode(configManifest));
if (! mo)
{
JLOG (j_.error()) << "Malformed manifest in config";

View File

@@ -365,6 +365,13 @@ ValidatorList::trustedPublisher (PublicKey const& identity) const
return identity.size() && publisherLists_.count (identity);
}
PublicKey
ValidatorList::localPublicKey () const
{
boost::shared_lock<boost::shared_mutex> read_lock{mutex_};
return localPubKey_;
}
bool
ValidatorList::removePublisherList (PublicKey const& publisherKey)
{

View File

@@ -23,8 +23,6 @@
#include <ripple/basics/BasicConfig.h>
#include <ripple/basics/base_uint.h>
#include <ripple/protocol/SystemParameters.h> // VFALCO Breaks levelization
#include <ripple/protocol/PublicKey.h> // NIKB Breaks levelization (TEMP)
#include <ripple/protocol/SecretKey.h> // NIKB Breaks levelization (TEMP)
#include <ripple/beast/net/IPEndpoint.h>
#include <beast/core/detail/ci_char_traits.hpp>
#include <ripple/beast/utility/Journal.h>
@@ -152,13 +150,8 @@ public:
int PATH_SEARCH_MAX = 10;
// Validation
PublicKey VALIDATION_PUB;
SecretKey VALIDATION_PRIV;
boost::optional<std::size_t> VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative
// Node Identity
std::string NODE_SEED;
std::uint64_t FEE_DEFAULT = 10;
std::uint64_t FEE_ACCOUNT_RESERVE = 200*SYSTEM_CURRENCY_PARTS;
std::uint64_t FEE_OWNER_RESERVE = 50*SYSTEM_CURRENCY_PARTS;

View File

@@ -66,7 +66,7 @@ struct ConfigSection
#define SECTION_VALIDATOR_LIST_KEYS "validator_list_keys"
#define SECTION_VALIDATOR_LIST_SITES "validator_list_sites"
#define SECTION_VALIDATORS "validators"
#define SECTION_VALIDATION_MANIFEST "validation_manifest"
#define SECTION_VALIDATOR_TOKEN "validator_token"
#define SECTION_VETO_AMENDMENTS "veto_amendments"
} // ripple

View File

@@ -358,22 +358,10 @@ void Config::loadFromString (std::string const& fileContents)
if (getSingleSection (secConfig, SECTION_SSL_VERIFY, strTemp, j_))
SSL_VERIFY = beast::lexicalCastThrow <bool> (strTemp);
if (getSingleSection (secConfig, SECTION_VALIDATION_SEED, strTemp, j_))
{
auto const seed = parseBase58<Seed>(strTemp);
if (!seed)
Throw<std::runtime_error> (
"Invalid seed specified in [" SECTION_VALIDATION_SEED "]");
VALIDATION_PRIV = generateSecretKey (KeyType::secp256k1, *seed);
VALIDATION_PUB = derivePublicKey (KeyType::secp256k1, VALIDATION_PRIV);
}
if (getSingleSection (secConfig, SECTION_NODE_SEED, NODE_SEED, j_))
{
if (!parseBase58<Seed>(NODE_SEED))
Throw<std::runtime_error> (
"Invalid seed specified in [" SECTION_NODE_SEED "]");
}
if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
Throw<std::runtime_error> (
"Cannot have both [" SECTION_VALIDATION_SEED "] "
"and [" SECTION_VALIDATOR_TOKEN "] config sections");
if (getSingleSection (secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
NETWORK_QUORUM = beast::lexicalCastThrow <std::size_t> (strTemp);

View File

@@ -1086,7 +1086,7 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMTransaction> const& m)
flags |= SF_TRUSTED;
}
if (! app_.config().VALIDATION_PUB.size())
if (! app_.getOPs().getValidationPublicKey().size())
{
// For now, be paranoid and have each validator
// check each transaction, regardless of source
@@ -1248,8 +1248,8 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
return;
}
if (app_.config().VALIDATION_PUB.size() &&
publicKey == app_.config().VALIDATION_PUB)
if (app_.getOPs().getValidationPublicKey().size() &&
publicKey == app_.getOPs().getValidationPublicKey())
{
JLOG(p_journal_.trace()) << "Proposal: self";
return;

View File

@@ -442,7 +442,6 @@ JSS ( validated ); // out: NetworkOPs, RPCHelpers, AccountTx*
JSS ( validated_ledger ); // out: NetworkOPs
JSS ( validated_ledgers ); // out: NetworkOPs
JSS ( validation_key ); // out: ValidationCreate, ValidationSeed
JSS ( validation_manifest ); // out: NetworkOPs
JSS ( validation_private_key ); // out: ValidationCreate
JSS ( validation_public_key ); // out: ValidationCreate, ValidationSeed
JSS ( validation_quorum ); // out: NetworkOPs

View File

@@ -227,7 +227,7 @@ public:
PublicKey emptyLocalKey;
std::vector<std::string> s1;
std::vector<std::string> keys;
std::vector<std::string> cfgManifest;
std::string cfgManifest;
for (auto const& man : inManifests)
s1.push_back (toBase58(
TokenType::TOKEN_NODE_PUBLIC, man->masterKey));
@@ -259,7 +259,7 @@ public:
}
{
// load config manifest
std::vector<std::string> const badManifest ({"bad manifest"});
std::string const badManifest = "bad manifest";
ManifestCache loaded;
BEAST_EXPECT(! loaded.load (
@@ -269,9 +269,8 @@ public:
auto const pk = derivePublicKey(KeyType::ed25519, sk);
auto const kp = randomKeyPair(KeyType::secp256k1);
std::vector<std::string> const cfgManifest ({
makeManifestString (pk, sk, kp.first, kp.second, 0)
});
std::string const cfgManifest =
makeManifestString (pk, sk, kp.first, kp.second, 0);
BEAST_EXPECT(loaded.load (
dbCon, "ValidatorManifests", cfgManifest));
@@ -367,6 +366,45 @@ public:
BEAST_EXPECT(cache.getMasterKey(kp1.first) == kp1.first);
}
void testValidatorToken()
{
testcase ("validator token");
{
auto const valSecret = parseBase58<SecretKey>(
TokenType::TOKEN_NODE_PRIVATE,
"paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi");
// Format token string to test trim()
std::vector<std::string> const tokenBlob = {
" eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n",
" \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl \n",
"\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE\n",
"\t hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t \t\n",
"bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n",
"hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n",
"NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n",
"VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n"
};
auto const manifest =
"JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/vCxHXXLplc2GnMhAkE1agqXxBwD"
"wDbID6OMSYuM0FDAlpAgNk8SKFn7MO2fdkcwRQIhAOngu9sAKqXYouJ+l2V0W+sAOkVB"
"+ZRS6PShlJAfUsXfAiBsVJGesaadOJc/aAZokS1vymGmVrlHPKWX3Yywu6in8HASQKPu"
"gBD67kMaRFGvmpATHlGKJdvDFlWPYy5AqDedFv5TJa2w0i21eq3MYywLVJZnFOr7C0kw"
"2AiTzSCjIzditQ8=";
auto const token = ValidatorToken::make_ValidatorToken(tokenBlob);
BEAST_EXPECT(token);
BEAST_EXPECT(token->validationSecret == *valSecret);
BEAST_EXPECT(token->manifest == manifest);
}
{
std::vector<std::string> const badToken = { "bad token" };
BEAST_EXPECT(! ValidatorToken::make_ValidatorToken(badToken));
}
}
void
run() override
{
@@ -428,6 +466,7 @@ public:
testLoadStore (cache);
testGetSignature ();
testGetKeys ();
testValidatorToken ();
}
};

View File

@@ -142,9 +142,9 @@ private:
auto const localMasterPublic = derivePublicKey(
KeyType::ed25519, localMasterSecret);
auto cfgManifest = makeManifestString (
localMasterPublic, localMasterSecret,
localSigningPublic, localSigningSecret, 1);
std::string const cfgManifest (makeManifestString (
localMasterPublic, localMasterSecret,
localSigningPublic, localSigningSecret, 1));
auto format = [](
PublicKey const &publicKey,
@@ -254,6 +254,7 @@ private:
BEAST_EXPECT(trustedKeys->load (
*localSigningPublic, cfgKeys, emptyCfgPublishers));
BEAST_EXPECT(trustedKeys->localPublicKey() == localSigningPublic);
BEAST_EXPECT(trustedKeys->listed (*localSigningPublic));
for (auto const& n : configList)
BEAST_EXPECT(trustedKeys->listed (n));
@@ -268,6 +269,7 @@ private:
BEAST_EXPECT(trustedKeys->load (
localSigningPublic, cfgKeys, emptyCfgPublishers));
BEAST_EXPECT(trustedKeys->localPublicKey() == localSigningPublic);
BEAST_EXPECT(trustedKeys->listed (localSigningPublic));
for (auto const& n : configList)
BEAST_EXPECT(trustedKeys->listed (n));
@@ -283,6 +285,7 @@ private:
BEAST_EXPECT(trustedKeys->load (
localSigningPublic, cfgKeys, emptyCfgPublishers));
BEAST_EXPECT(trustedKeys->localPublicKey() == localMasterPublic);
BEAST_EXPECT(trustedKeys->listed (localSigningPublic));
BEAST_EXPECT(trustedKeys->listed (localMasterPublic));
for (auto const& n : configList)

View File

@@ -474,6 +474,45 @@ port_wss_admin
}
}
void testValidatorKeys ()
{
testcase ("validator keys");
std::string const validationSeed = "spA4sh1qTvwq92X715tYyGQKmAKfa";
auto const token =
"eyJ2YWxpZGF0aW9uX3ByaXZhdGVfa2V5IjoiOWVkNDVmODY2MjQxY2MxOGEyNzQ3Yj"
"U0Mzg3YzA2MjU5MDc5NzJmNGU3MTkwMjMxZmFhOTM3NDU3ZmE5ZGFmNiIsIm1hbmlm"
"ZXN0IjoiSkFBQUFBRnhJZTFGdHdtaW12R3RIMmlDY01KcUM5Z1ZGS2lsR2Z3MS92Q3"
"hIWFhMcGxjMkduTWhBa0UxYWdxWHhCd0R3RGJJRDZPTVNZdU0wRkRBbHBBZ05rOFNL"
"Rm43TU8yZmRrY3dSUUloQU9uZ3U5c0FLcVhZb3VKK2wyVjBXK3NBT2tWQitaUlM2UF"
"NobEpBZlVzWGZBaUJzVkpHZXNhYWRPSmMvYUFab2tTMXZ5bUdtVnJsSFBLV1gzWXl3"
"dTZpbjhIQVNRS1B1Z0JENjdrTWFSRkd2bXBBVEhsR0tKZHZERmxXUFl5NUFxRGVkRn"
"Y1VEphMncwaTIxZXEzTVl5d0xWSlpuRk9yN0Mwa3cyQWlUelNDakl6ZGl0UTg9In0=";
{
Config c;
static boost::format configTemplate (R"rippleConfig(
[validation_seed]
%1%
[validator_token]
%2%
)rippleConfig");
std::string error;
auto const expectedError =
"Cannot have both [validation_seed] "
"and [validator_token] config sections";
try {
c.loadFromString (boost::str (
configTemplate % validationSeed % token));
} catch (std::runtime_error& e) {
error = e.what();
}
BEAST_EXPECT(error == expectedError);
}
}
void testValidatorsFile ()
{
testcase ("validators_file");
@@ -818,6 +857,7 @@ trustthesevalidators.gov
{
testLegacy ();
testDbPath ();
testValidatorKeys ();
testValidatorsFile ();
testSetup (false);
testSetup (true);

View File

@@ -24,6 +24,7 @@
#include <ripple/ledger/OpenView.h>
#include <ripple/ledger/PaymentSandbox.h>
#include <ripple/ledger/Sandbox.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/Feature.h>
#include <type_traits>
@@ -809,12 +810,9 @@ class GetAmendments_test
setupConfigForUnitTests(*p);
// If the config has valid validation keys then we run as a validator.
auto const seed = parseBase58<Seed>("shUwVw52ofnCUX5m7kPTKzJdr4HEH");
if (!seed)
Throw<std::runtime_error> ("Invalid seed specified");
p->VALIDATION_PRIV = generateSecretKey (KeyType::secp256k1, *seed);
p->VALIDATION_PUB =
derivePublicKey (KeyType::secp256k1, p->VALIDATION_PRIV);
p->section(SECTION_VALIDATION_SEED).append(
std::vector<std::string>{"shUwVw52ofnCUX5m7kPTKzJdr4HEH"});
return p;
}

View File

@@ -22,6 +22,7 @@
#include <test/jtx/TestSuite.h>
#include <ripple/overlay/Cluster.h>
#include <ripple/overlay/ClusterNode.h>
#include <ripple/protocol/SecretKey.h>
namespace ripple {
namespace tests {

View File

@@ -29,18 +29,18 @@ namespace ripple {
namespace test {
namespace validator {
static auto const seed = "ss7t3J9dYentEFgKdPA3q6eyxtrLB";
static auto const master_key =
"nHUYwQk8AyQ8pW9p4SvrWC2hosvaoii9X54uGLDYGBtEFwWFHsJK";
static auto const signing_key =
"n9LHPLA36SBky1YjbaVEApQQ3s9XcpazCgfAG7jsqBb1ugDAosbm";
// Format manifest string to test trim()
static auto const manifest =
" JAAAAAFxIe2cDLvm5IqpeGFlMTD98HCqv7+GE54anRD/zbvGNYtOsXMhAuUTyasIhvj2KPfN\n"
" \tRbmmIBnqNUzidgkKb244eP794ZpMdkYwRAIgNVq8SYP7js0C/GAGMKVYXiCGUTIL7OKPSBLS \n"
"\t7LTyrL4CIE+s4Tsn/FrrYj0nMEV1Mvf7PMRYCxtEERD3PG/etTJ3cBJAbwWWofHqg9IACoYV\n"
"\t +n9ulZHSVRajo55EkZYw0XUXDw8zcI4gD58suOSLZTG/dXtZp17huIyHgxHbR2YeYjQpCw==\t \t";
static auto sequence = 1;
static auto const public_key =
"nHBt9fsb4849WmZiCds4r5TXyBeQjqnH5kzPtqgMAQMgi39YZRPa";
static auto const token =
"eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n"
"QzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl\n"
"c3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE\n"
"hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\n"
"bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n"
"hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n"
"NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n"
"VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n";
}
class ServerInfo_test : public beast::unit_test::suite
@@ -52,18 +52,15 @@ public:
{
auto p = std::make_unique<Config>();
boost::format toLoad(R"rippleConfig(
[validation_manifest]
[validator_token]
%1%
[validation_seed]
%2%
[validators]
%3%
%2%
)rippleConfig");
p->loadFromString (boost::str (
toLoad % validator::manifest % validator::seed % validator::master_key));
toLoad % validator::token % validator::public_key));
setupConfigForUnitTests(*p);
@@ -88,15 +85,7 @@ public:
BEAST_EXPECT(result[jss::status] == "success");
BEAST_EXPECT(result[jss::result].isMember(jss::info));
BEAST_EXPECT(result[jss::result][jss::info]
[jss::pubkey_validator] == validator::signing_key);
BEAST_EXPECT(result[jss::result][jss::info].isMember(
jss::validation_manifest));
BEAST_EXPECT(result[jss::result][jss::info][jss::validation_manifest]
[jss::master_key] == validator::master_key);
BEAST_EXPECT(result[jss::result][jss::info][jss::validation_manifest]
[jss::signing_key] == validator::signing_key);
BEAST_EXPECT(result[jss::result][jss::info][jss::validation_manifest]
[jss::seq] == validator::sequence);
[jss::pubkey_validator] == validator::public_key);
}
}

View File

@@ -18,6 +18,7 @@
#include <BeastConfig.h>
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/JsonFields.h>
#include <test/jtx/WSClient.h>
#include <test/jtx.h>
@@ -313,26 +314,14 @@ public:
static
std::unique_ptr<Config>
makeValidatorConfig(
std::string const& valPrivateKey, std::string const& valPublicKey)
makeValidatorConfig(std::string const& seed)
{
auto p = std::make_unique<Config>();
setupConfigForUnitTests(*p);
// If the config has valid validation keys then we run as a validator.
auto const sk = parseBase58<SecretKey>(
TOKEN_NODE_PRIVATE,
valPrivateKey);
if (!sk)
Throw<std::runtime_error> ("Invalid validation private key");
p->VALIDATION_PRIV = *sk;
auto const pk = parseBase58<PublicKey>(
TOKEN_NODE_PUBLIC,
valPublicKey);
if (!pk)
Throw<std::runtime_error> ("Invalid validation public key");
p->VALIDATION_PUB = *pk;
p->section(SECTION_VALIDATION_SEED).append(
std::vector<std::string>{seed});
return p;
}
@@ -342,11 +331,10 @@ public:
using namespace jtx;
// Public key must be derived from the private key
const std::string valPrivateKey =
"paEdUCVVCNnv4aYBepid9Xh3NaAr9xWRw2vh351piFJrxQwvExd";
const std::string valPublicKey =
"n9MvFGjgv1kYkm7bLbb2QUwSqgzrQkYMYHXtrzN8W28Jfp2mVihq";
Env env(*this, makeValidatorConfig(valPrivateKey, valPublicKey));
std::string const seed = "snpTg5uPtiRG2hE8HHCAF4NzdorKT";
std::string const valPublicKey =
"n9KCD2WU48u1WG3neBH6vRSinAxoTwrjLbjUAn6Xq6mCe5YrJv2V";
Env env(*this, makeValidatorConfig(seed));
auto wsc = makeWSClient(env.app().config());
Json::Value stream;