-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace manifest with validator token (RIPD-1386) #1975
Conversation
Codecov Report@@ Coverage Diff @@
## develop #1975 +/- ##
===========================================
+ Coverage 67.31% 67.31% +<.01%
===========================================
Files 686 690 +4
Lines 49320 49729 +409
===========================================
+ Hits 33201 33477 +276
- Misses 16119 16252 +133
Continue to review full report at Codecov.
|
std::memcmp(lhs.data(), | ||
rhs.data(), rhs.size()) == 0; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In alignment with the principle of least surprise, it's often a good idea to supply !=
if a type supplies ==
. The implementation could be as simple as...
inline
bool
operator!= (SecretKey const& lhs,
SecretKey const& rhs)
{
return ! (lhs == rhs);
}
@@ -153,7 +145,7 @@ class SecretKey_test : public beast::unit_test::suite | |||
"pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); | |||
BEAST_EXPECT(sk2); | |||
|
|||
BEAST_EXPECT(equal (sk1, *sk2)); | |||
BEAST_EXPECT(sk1 == *sk2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicely done with the testing.
I have a meta question before I review the second commit. Somehow I got the impression this pull request was attempting to move away from the term "manifest". But I'm still seeing "manifest" all over the second commit. If we end up with something that we call a "manifest" internally but we give it another name in the config file, well, I'm not very excited about that prospect. I think that would make the code harder to maintain and to talk about. The new name in the config file would need to be dramatically better than "manifest", and the cost of changing the the internal names away from "manifest" would need to be very high. Can you give me a summary of your plans and intentions? Thanks. |
My goal is to replace "manifest" with "token" as far the user is concerned. The current implementation has the token as an encoded json object containing "validation_private_key" and "manifest" fields. We can't simply replace Maybe the PR/commit message should be changed to "Add validator token". I also realize the Python tool and its manifest-tool-guide both still have manifest in the names. I anticipate both of those being removed in favor of the ripple-libpp based tool. What do you think about them being removed now? |
@wilsonianb and I talked off line. Here's my takeaway. The old The (new) When rippled parses the |
# validation_public_key: n9LtZ9haqYMbzJ92cDd3pu3Lko6uEznrXuYea3ehuhVcwDHF5coX | ||
# sequence number: 1 | ||
``` | ||
|
||
A manifest is a signed message used to inform other servers of this validator's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the descriptions in this file that could be significantly improved given the changes you're making. However I'm not going to dig into this file in hopes that this all goes away when the ripple-libpp based tool is released in concert with these changes.
doc/rippled-example.cfg
Outdated
# validation without having to store the validator keys on the network | ||
# connected server. The field should contain a JSON object with two fields: | ||
# "validation_private_key" and "manifest". The first contains a hex-encoded | ||
# private key used for signing validations. The second contains a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this paragraph contains way too much information. The presence of the validation_private_key
and manifest
is completely invisible to someone editing the config. All they should see is a base64 encoded blob. Tell them that the field is a blob that is generated by external tools. Otherwise you raise too many questions.
src/ripple/app/main/Application.cpp
Outdated
config().section (SECTION_VALIDATORS).values (), | ||
config().section (SECTION_VALIDATOR_LIST_KEYS).values (), | ||
config().section (SECTION_VALIDATION_MANIFEST).lines())) | ||
config().VALIDATION_MANIFEST)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a VALIDATION_MANIFEST
section of the config? Somehow I don't think the config should be responsible for ripping apart the [validator_token]
.
src/ripple/core/impl/Config.cpp
Outdated
for (auto const& line : section (SECTION_VALIDATOR_TOKEN).lines()) | ||
tokenStr += beast::rfc2616::trim(line); | ||
|
||
tokenStr = beast::detail::base64_decode(tokenStr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Um, this feels odd to me. We have a JSON formatted object encoded in base64. Partly it is unlike any of the rest of the config. This is the only place where the config decodes base64. This is also the only place that the config parses JSON.
I don't have a problem with the config handling mysterious encoded text; the manifest was doing that before. But it feels weird to me that the config is doing the decoding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having the config do this since it is currently where VALIDATION_PRIV
and VALIDATION_PUB
are stored, and that information is now in the token.
I'll see if the key pair could be stored somewhere else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd probably feel okay with the config returning a reference to a const ValidatorToken
object that did the parsing and kept the data. It just feels funny for the config to be so intimate with the details of a non-human-readable field. You could also try to convince me that what the config is doing now is okay, if it doesn't feel odd to you.
ebc9fb4
to
b2ccffd
Compare
Rebased on dynamic-unl branch, and added a fold commit that moves validator key and token parsing out of |
b2ccffd
to
03803d1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like an improvement to me. I left some notes about const
and naming and the like. But I don't believe I have any structural concerns at the moment. Nice work.
src/ripple/app/main/Application.cpp
Outdated
PublicKey validationPub; | ||
SecretKey validationPriv; | ||
std::string manifest; | ||
if (config().exists (SECTION_VALIDATOR_TOKEN)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a check that returns an error if both SECTION_VALIDATOR_TOKEN
and SECTION_VALIDATION_SEED
are present in the config?
Oh, never mind. That's handled in Config.cpp where it should be.
src/ripple/app/main/Application.cpp
Outdated
"Invalid entry in validator configuration."; | ||
return false; | ||
PublicKey validationPub; | ||
SecretKey validationPriv; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we name this variable validationSec
or validationSecret
?. It seems like we're migrating toward calling the non-public key a "secret" key instead of a "private" key.
src/ripple/app/main/Application.cpp
Outdated
"Invalid seed specified in [" SECTION_VALIDATION_SEED "]"); | ||
validationPriv = generateSecretKey (KeyType::secp256k1, *seed); | ||
validationPub = derivePublicKey (KeyType::secp256k1, validationPriv); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel better about this code being here in the Application than I did with it in Config. How does it feel to you?
src/ripple/app/misc/NetworkOPs.cpp
Outdated
{ | ||
return mLedgerConsensus->getValidationPublicKey (); | ||
} | ||
void setValidationKeys (SecretKey validationPriv, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, maybe this parameter should be renamed from validationPriv
to validationSec
or validationSecret
. Or, since the method name already tells us were setting "ValidationKeys", possibly just secret
, and public
...?
src/ripple/app/misc/Manifest.h
Outdated
std::string manifest; | ||
SecretKey validationPrivate; | ||
|
||
ValidatorToken(std::string m, SecretKey valPriv); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't expect the ValidatorToken
constructor to be used by anything other than make_ValidatorToken
, right? So I think the constructor should probably be private
.
Do you want a copy constructor? If not you should probably declare it = delete
.
Can you see if the manifest
and validationPrivate
members (er, should that be validationSecret
?) could be const
?
{ | ||
try | ||
{ | ||
std::string tokenStr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know the maximum expected size of tokenStr
? If not you could find it by traversing tokenBlob
. Once you know the size you can reserve it and avoid many trips to the heap. Perhaps something like:
tokenStr.reserve (
std::accumulate (tokenBlob.cbegin(), tokenBlob.cend(), std::size_t(0),
[] (std::size_t init, std::string const& s)
{
return init + s.size();
}));
public: | ||
/** Returns validation public key */ | ||
PublicKey | ||
getValidationPublicKey () override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method should be const
. Does this want to return a PublicKey const&
(avoid a copy)?
src/test/rpc/Subscribe_test.cpp
Outdated
@@ -342,11 +331,10 @@ class Subscribe_test : public beast::unit_test::suite | |||
using namespace jtx; | |||
|
|||
// Public key must be derived from the private key | |||
const std::string valPrivateKey = | |||
"paEdUCVVCNnv4aYBepid9Xh3NaAr9xWRw2vh351piFJrxQwvExd"; | |||
const std::string seed = "snpTg5uPtiRG2hE8HHCAF4NzdorKT"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: usually in this code base the const
comes second. That's dissimilar from most code bases I've worked in before.
"paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi"); | ||
|
||
// Format token string to test trim() | ||
std::vector<std::string> const tokenBlob = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a single large string in a std::vector<std::string>
. Is that what you want? If you want 8 strings in the vector
you need to enclose each string in {}
and put commas between.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like just commas is sufficient. I get 8 for tokenBlob.size()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Glad that worked. Thanks for checking.
src/ripple/app/misc/NetworkOPs.h
Outdated
@@ -177,6 +177,9 @@ class NetworkOPs | |||
|
|||
// FIXME(NIKB): Remove the need for this function | |||
virtual void setLastCloseTime (NetClock::time_point t) = 0; | |||
virtual PublicKey getValidationPublicKey () = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be const
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there are a couple of private -> secret conversions that I missed. I tagged those. Other than that things look good.
src/ripple/app/main/Application.cpp
Outdated
std::string manifest; | ||
if (config().exists (SECTION_VALIDATOR_TOKEN)) | ||
{ | ||
if (auto const token = ValidatorToken::make_ValidatorToken ( | ||
config().section (SECTION_VALIDATOR_TOKEN).lines ())) | ||
{ | ||
validationPriv = token->validationPrivate; | ||
validationPub = derivePublicKey (KeyType::secp256k1, validationPriv); | ||
valSecret = token->validationPrivate; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all of the renames private -> secret. It looks like I may have missed a few. How would you feel about renaming the validatorToken::validationPrivate
field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think the "validation_private_key"
json field in the token should also be renamed?
https://github.com/wilsonianb/rippled/blob/rename-manifests/src/ripple/app/misc/impl/Manifest.cpp#L176
One of the reasons I had been using "private" is that the key uses the TOKEN_NODE_PRIVATE
base58 encoding.
https://github.com/ripple/rippled/blob/develop/src/ripple/protocol/tokens.h#L33
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Legacy names I'm not so confident about, particularly if they may have leaked into our API. Mostly I'm reacting to how our newer code seems to prefer "secret" over "private". Where it's possible I'd like to continue that trend toward "secret" so we don't end up with an ugly mix. Just my take on it.
src/ripple/app/misc/Manifest.h
Outdated
@@ -133,7 +133,12 @@ struct ValidatorToken | |||
std::string manifest; | |||
SecretKey validationPrivate; | |||
|
|||
ValidatorToken(std::string m, SecretKey valPriv); | |||
private: | |||
ValidatorToken(std::string const& m, SecretKey const& valPriv); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SecretKey
member should maybe be names validationSecret
? And maybe rename the constructor argument to valSecret
?
@@ -143,7 +143,7 @@ Blob Manifest::getMasterSignature () const | |||
return st.getFieldVL (sfMasterSignature); | |||
} | |||
|
|||
ValidatorToken::ValidatorToken(std::string m, SecretKey valPriv) | |||
ValidatorToken::ValidatorToken(std::string const& m, SecretKey const& valPriv) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe rename the argument to valSecret
?
b6d4eda
to
6199e03
Compare
@@ -757,30 +739,14 @@ LedgerMaster::checkAccept (uint256 const& hash, std::uint32_t seq) | |||
} | |||
|
|||
/** | |||
* Determines how many validations are needed to fully-validated a ledger | |||
* Determines how many validations are needed to fully-validate a ledger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Micro-nit: remove the "-" between "fully" and "validate"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed hyphen
bff196f#diff-a7b89eed73c070e3b601e2d4de8bf4baR742
auto constexpr DEFAULT_REFRESH_INTERVAL = std::chrono::minutes{5}; | ||
|
||
ValidatorSite::ValidatorSite ( | ||
Application& app, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only use app
to get the IO service and once to call app_.validators()
. We can cut out the dependency on the app object if we change this constructor to accept the I/O service to be used and a ValidatorList&
.
The call in the Application
constructor can be changed to this:
, validatorSites_ (std::make_unique<ValidatorSite> (
get_io_service (), *validators_, logs_->journal("ValidatorSite")))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed Application
from ValidatorSite
f0940f1
Instead of specifying a static list of trusted validators in the config or validators file, the configuration can now include trusted validator list publisher keys. The trusted validator list and quorum are now reset each consensus round using the latest validator lists and the list of recent validations seen. The minimum validation quorum is now only configurable via the command line.
Validator lists from configured remote sites are fetched at a regular interval. Fetched lists are expected to be in JSON format and contain the following fields: * "manifest": Base64-encoded serialization of a manifest containing the validator publisher's master and signing public keys. * "blob": Base64-encoded JSON string containing a "sequence", "expiration" and "validators" field. "expiration" contains the Ripple timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when the list expires. "validators" contains an array of objects with a "validation_public_key" field. * "signature": Hex-encoded signature of the blob using the publisher's signing key. * "version": 1 * "refreshInterval" (optional)
6199e03
to
4ae8fa6
Compare
Squashed, rebased on the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Looks good to me.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider changing the output of the manifest tool to include this warning, and format it in a way that makes it less likely for someone to copy-paste the whole thing in the config file accidentally.
For example:
### MASTER SECRET KEY
### PLEASE SAFEGUARD THE MASTER SECRET KEY AND DO NOT
### INSTALL IT IN THE CONFIG FILE.
###
### [master_secret]
### paamfhAn5m1NM4UUu5mmvVaHQy8Fb65bkpbaNrvKwX3YMKdjzi2
### MASTER PUBLIC KEY
[validator_keys]
nHDwNP6jbJgVKj5zSEjMn8SJhTnPV54Fx5sSiNpwqLbb42nmh3Ln
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd change it if it didn't affect the python tests.
I'm hoping to have the ripple-libpp based tool ready soon, which will remove the need to copy and paste these keys anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My hope as well. The ripple-libpp based tool is much cleaner.
Moving commits to #1842. Thanks |
Replaces
[validation_manifest]
config section with[validator_token]
.[validation_manifest]
was not documented in rippled-example.cfg or the dev portal, and there is no evidence of it being used externally by validator operators.Removes admin-only
validation_manifest
field fromserver_info
The changes only consist of the last two commits:
I've based this on the the dynamic UNL branch (#1842) to minimize conflicts. Let me know if there's a better way to submit this.
Reviewers: @nbougalis @scottschurr