-
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
Update validations on UNL change (RIPD-1566) #2393
Conversation
Jenkins Build SummaryBuilt from this commit Built at 20180301 - 05:36:53 Test Results
|
Rebased on 0.90.0 |
src/ripple/protocol/STValidation.h
Outdated
|
||
@param ledgerHash The hash of the validated ledger | ||
@param signTime When the validation is signed | ||
@param raPub The current signing public key |
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 realize this isn't new but maybe change raPub
to publicKey
here since that's what's used in the definition, and I don't know what raPub
means
src/ripple/protocol/STValidation.h
Outdated
@param ledgerHash The hash of the validated ledger | ||
@param signTime When the validation is signed | ||
@param raPub The current signing public key | ||
@param nodeID ID corresponding to nodes public master key |
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.
nodes
-> node's
@@ -172,7 +172,7 @@ RCLValidationsAdaptor::onStale(RCLValidation&& v) | |||
} | |||
|
|||
void | |||
RCLValidationsAdaptor::flush(hash_map<PublicKey, RCLValidation>&& remaining) | |||
RCLValidationsAdaptor::flush(hash_map<NodeID, RCLValidation>&& remaining) |
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 docs should also be updated
https://github.com/bachase/rippled/blob/adef962292739c2e732d253c11cb168fa92e48c6/src/ripple/consensus/Validations.h#L260
src/ripple/consensus/Validations.h
Outdated
if (!enforcer(now, val.seq(), parms_)) | ||
return ValStatus::badSeq; | ||
|
||
// This validation is a repeat if we already have | ||
// one with the same id for this key | ||
auto const ret = byLedger_[val.ledgerID()].emplace(key, val); | ||
// one with the same id and signing key for this node |
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.
would it also be a repeat if we already have one with the same id and a different signing key for this node?
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 repeatedID
actually should have been removed as part of the prefer by branch work (which you suggested #2300 (comment)). If a node issues two validations with the same ID (using either the same or different signing keys), the seq enforcer above will ignore all but the first.
Agree?
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.
How about (the unexpected case) when there's multiple validations with the same ledger id from the same validator but with different sequences?
We could just treat it normally (add the new validation assuming it has a higher sequence) and not need repeatedID
.
I was mainly asking because I don't think this change to the comment is accurate.
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 believe the ledger sequence number is part of what is hashed to create the ledger id, so you can't really have two validations for the same id but different sequence numbers.
I agree that the comment is now inaccurate.
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.
Right, it would be a malformed validation, but that doesn't mean someone won't send one.
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.
Good point.
I like
We could just treat it normally (add the new validation assuming it has a higher sequence) and not need repeatedID.
I don't see repeatedID
really giving us much useful information; the increasing sequence number invariant is the essential one.
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.
Actually, what would happen if a trusted validator only sends validations for a single id but with incrementing sequence numbers?
It seems like it would be as if they only validated that single id, but that validation never expires/is always current (assuming they keep resigning and updated the sign time). If we ever stopped keeping that ledger (via online delete?) we would keep re-acquiring it in order to update the ledger trie.
Would any of that be an issue?
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.
If we were unable to acquire it, that validator's validation would not be added to the trie and effectively be ignored. Impact wise, I don't see this as different than a validator sending validations for random ledger ids that we can't acquire.
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 wondering about if it's a valid ledger id that we can acquire.
If we haven't acquired it yet, and we have a prior validation with the same id but lower seq, we would cancel and then restart the acquire attempt. hit InboundLedgers
multiple times for the same ledger
If/once we have acquired the ledger, we would have this entry in the trie with the real seq that persists until the bad validator stops sending the validations.
src/ripple/protocol/STValidation.h
Outdated
|
||
/** Construct a STValidation from a peer. | ||
|
||
Construct an STValidation from serialized data previously shared by 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.
nit: either an
-> a
or a
-> an
above
break; | ||
newTrustedKeys.insert(val.second); | ||
|
||
if (trustedKeys_.count(val.second) == 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.
You could change this to
if (trustedKeys_.erase(val.second) == 0)
and then you could remove the if(newTrustedKeys.count(k) == 0)
check in the loop below
void | ||
onConsensusStart ( | ||
KeySet const& seenValidators); | ||
TrustChanges |
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 this warrants a name change to onConsensusStart
?
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.
Good call; updateTrustedKeys
matches the doxygen comment.
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.
Actually, that was already out of sync a bit based on these changes. updateTrusted
is better.
src/test/app/ValidatorList_test.cpp
Outdated
calcNodeID(*parseBase58<PublicKey>( | ||
TokenType::TOKEN_NODE_PUBLIC, valKey))); | ||
if(ins.second) | ||
initiallyAdded.insert(*ins.first); |
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 got confused by initiallyAdded
. It seems to actually represent the second/subsequent set of added trusted keys.
trustChanges.removed.insert(calcNodeID(k)); | ||
|
||
quorum_ = quorum; | ||
std::swap(newTrustedKeys, trustedKeys_); |
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 really need to swap here, this is really just a way to quickly move newTrustedKeys
into trustedKeys_
. We can do that directly by putting newTrustedKeys
into it's own scope and then trustedKeys_ = std::move(newTrustedKeys)
here instead. Fine as-is though.
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.
Your suggestion is clearer for anyone reading the code, so I will change.
src/ripple/protocol/STValidation.h
Outdated
*/ | ||
STValidation( | ||
SerialIter& sit, | ||
std::function<NodeID(PublicKey const&)> const& lookupNodeID, |
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 don't love std::function
if it can be avoided (it type erases and may allocate). This function is used to calculate a NodeID
. One solution: we could make this parameter a template and then delegate to a private constructor that takes the NodeID
directly. That way you don't have to put the much of an implementation in the header file.
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 PublicKey
that is the argument to the function requires initializing the STObject
base class first, so I had trouble delegating to a private constructor. I think putting everything in the header and using a template isn't so bad in this case.
src/test/app/RCLValidations_test.cpp
Outdated
void | ||
run() override | ||
testChangeTrusted() | ||
{ |
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.
Give the test a name with testcase
src/test/app/RCLValidations_test.cpp
Outdated
} | ||
|
||
void | ||
testRCLValidatedLedger() | ||
{ |
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.
Give the test a name with testcase
static hash_set<NodeID> | ||
asNodeIDs(std::initializer_list<PublicKey> const& pks) | ||
{ | ||
hash_set<NodeID> res; |
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: We know the final size of res. We could res.reserve(pks.size());
here. Tho I'm less concerned about this sort of thing in test code.
trustedVals.clear(); | ||
harness.vals().trustChanged({}, {a.nodeID()}); | ||
// make acquiring ledger available | ||
Ledger ledgerAB = h["ab"]; |
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.
ledgerAB
is an unused variable
@@ -530,7 +530,7 @@ class ValidatorList_test : public beast::unit_test::suite | |||
|
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 test could also be renamed to testUpdateTrusted
|
||
if (checkSignature && !isValid()) | ||
{ | ||
JLOG(debugLog().error()) << "Invalid validation" << getJson(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.
error: there are no arguments to 'debugLog' that depend on a template parameter, so a declaration of 'debugLog' must be available [-fpermissive]
JLOG(debugLog().error()) << "Invalid validation" << getJson(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.
We need to include #include <ripple/basics/Log.h>
in this file.
src/ripple/consensus/Validations.h
Outdated
auto const ret = byLedger_[val.ledgerID()].emplace(key, val); | ||
if (!ret.second && ret.first->second.key() == val.key()) | ||
return ValStatus::repeatID; | ||
// one with the same id and signing key for this node |
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 the comment be removed or modified to say that we don't reject a validation with the same id as a previous one with a lower sequence?
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.
Oops, forgot to remove.
src/ripple/consensus/Validations.h
Outdated
// one with the same id and signing key for this node | ||
auto ret = byLedger_[val.ledgerID()].emplace(nodeID, val); | ||
if (!ret.second) | ||
ret.first->second = val; |
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 usually see code like this written as something like: byLedger_[ledgerID][nodeID] = val
. What you have is better (and doesn't require val to be default constructable). Maybe leave a comment that we should use c++-17's insert_or_assign
here when we move to c++-17. This both clarifies the intent here and reminds us to clean this up when we switch.
Codecov Report
@@ Coverage Diff @@
## develop #2393 +/- ##
===========================================
+ Coverage 70.3% 70.32% +0.01%
===========================================
Files 703 703
Lines 53671 53715 +44
===========================================
+ Hits 37735 37776 +41
- Misses 15936 15939 +3
Continue to review full report at Codecov.
|
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.
👍
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.
LGTM 👍
src/test/app/ValidatorList_test.cpp
Outdated
@@ -535,15 +546,18 @@ class ValidatorList_test : public beast::unit_test::suite | |||
cfgKeys.push_back (toBase58( | |||
TokenType::TOKEN_NODE_PUBLIC, valKey)); | |||
if (cfgKeys.size () <= n - 5) | |||
activeValidators.emplace (valKey); | |||
activeValidators.emplace (calcNodeID(valKey)); | |||
} | |||
|
|||
BEAST_EXPECT(trustedKeys->load ( | |||
emptyLocalKey, cfgKeys, cfgPublishers)); | |||
|
|||
// onConsensusStart should make all available configured |
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.
stray onConsensusStart
Change the trust status of existing validations based when nodes are added or removed from the UNL.
Squashed. |
Merged in 20defb4 |
When validators are removed or added via dynamic UNL change, these changes ensure a node updates the trust status of any existing validations.
The first set of changes switch to use
NodeID
to consistently refer to the sender of consensus proposals and validations even when using manifests.