-
Notifications
You must be signed in to change notification settings - Fork 72
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
IF: Implement capabilities to modify, reorder, drop, and duplicate votes and add comprehensive vote processing Boost unit tests #2234
Conversation
… repeats time; add more verification
…s; change clusetr to tester which makes more sense
Note:start |
…tiple tester instances
libraries/chain/block_state.cpp
Outdated
return {status, strong ? core.final_on_strong_qc_block_num : std::optional<uint32_t>{}}; | ||
return {status, | ||
(status == vote_status::success && strong) ? | ||
core.final_on_strong_qc_block_num : std::optional<uint32_t>{}}; |
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.
Why this change? It is not needed imo.
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.
When it returns a non-null-optional, the caller will go ahead to call set_if_irreversible_block_num
which is a waste. I also think it is cleaner: if a vote is bad, both return values (status
and strong
) should be set properly so that they cannot be used. @heifner ?
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 my branch I have this as:
std::optional<uint32_t> new_lib{};
if (status == vote_status::success && state == pending_quorum_certificate::state_t::strong)
new_lib = core.final_on_strong_qc_block_num;
return {status, state, new_lib};
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 fine with Kevin's version.
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 occurs to me that this is not correct. It assumes only votes for this->core
comes in. This really should be updated_core
. I will make that change in my PR.
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.
Honestly, I liked it better before any change. The meaning was clear. We're asking block_state
to aggregate a vote into its pending_qc
, and it returns a pair where:
pair.first
is the status of applying the vote (so we know whether to propagate the vote)
pair.second
is the new lib
that pending_qc
certifies (if it achieved strong status). The caller decides what to do with this info.
I'm not understanding why it helps to make it more complicated than that.
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 changing in my PR. We have to let fork_db tell us new lib.
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.
Isn't still fine to let aggregate_vote
tell you which block its pending_qc
is making final, even if that's not the lib that the controller will end up storing?
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.
Potentially, but as is it is not telling you the correct answer because it is not correctly taking previous votes into account. It is only looking at core
not updated_core
.
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 not understanding what updated_core
is about? I thought you may need to update the core of other block state (the descendants), but why the one we are voting on?
unittests/finality_tests.hpp
Outdated
eosio::testing::tester node1; | ||
eosio::testing::tester node2; | ||
eosio::testing::tester node3; | ||
uint32_t node1_prev_lib_bum {0}; | ||
uint32_t node2_prev_lib_bum {0}; | ||
uint32_t node3_prev_lib_bum {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.
Having a std::array<node_info, 3>
with
struct node_info {
eosio::testing::tester node;
uint32_t node_prev_lib_bum;
};
would remove code duplication (in member functions and elsewhere).
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. Done
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.
Also move the implementation out of .hpp 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.
Somewhat minor, but I wonder if you shouldn't name them node0, node1, node2
if you use the array approach so they match index.
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.
Yes, I will make the change. That reduces confusion about which node is which. Thanks.
…ion; use array to represent nodes to reduce duplicate code
…and reduce confusion; refactor return code handling in aggregate_vote
} | ||
|
||
// called by add_vote, already protected by mutex | ||
vote_status pending_quorum_certificate::add_strong_vote(const std::vector<uint8_t>& proposal_digest, size_t index, | ||
const bls_public_key& pubkey, const bls_signature& sig, | ||
uint64_t weight) { | ||
if (auto s = _strong_votes.add_vote(proposal_digest, index, pubkey, sig); s != vote_status::success) | ||
if (auto s = _strong_votes.add_vote(proposal_digest, index, pubkey, sig); s != vote_status::success) { | ||
dlog("add_strong_vote returned failure"); |
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.
why is this dlog
useful since we have a non-conditional dlog
in add_vote
?
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 only happening when duplicate occurs; useful for debugging.
unittests/finality_test_cluster.hpp
Outdated
static constexpr size_t node0 = 0; // node0 index in nodes array | ||
static constexpr size_t node1 = 1; // node1 index in nodes array | ||
static constexpr size_t node2 = 2; // node2 index in nodes array |
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.
Code will be cleaner if you define these instead:
node_info& node0 = node[0];
node_info& node1 = node[1];
node_info& node2 = node[2];
and:
void setup_node(node_info& node, eosio::chain::account_name local_finalizer);
bool lib_advancing(node_info& node);
eosio::chain::vote_status process_vote(node_info& node, size_t vote_index, vote_mode mode);
eosio::chain::vote_status process_vote(node_info& node, vote_mode mode);
Code will look like this:
finality_test_cluster::finality_test_cluster() {
using namespace eosio::testing;
setup_node(node0, "node0"_n);
setup_node(node1, "node1"_n);
setup_node(node2, "node2"_n);
// collect node1's votes
node1.node.control->voted_block().connect( [&]( const eosio::chain::vote_message& vote ) {
node1.votes.emplace_back(vote);
});
// collect node2's votes
node2.node.control->voted_block().connect( [&]( const eosio::chain::vote_message& vote ) {
node2.votes.emplace_back(vote);
});
...
unittests/finality_test_cluster.cpp
Outdated
for (size_t i = 0; i < node.size(); ++i) { | ||
node[i].votes.clear(); | ||
node[i].prev_lib_num = node[i].node.control->if_irreversible_block_num(); | ||
} |
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.
for (size_t i = 0; i < node.size(); ++i) { | |
node[i].votes.clear(); | |
node[i].prev_lib_num = node[i].node.control->if_irreversible_block_num(); | |
} | |
for (auto& n : nodes { | |
n.votes.clear(); | |
n.prev_lib_num = n.node.control->if_irreversible_block_num(); | |
} |
Implemented a test cluster consisting of 3 nodes (controllers) with capabilities to modify, reorder, drop, and duplicate votes:
Resolved #2193 and #2147