Skip to content
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: Filter duplicate votes #1672

Merged
merged 3 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libraries/hotstuff/include/eosio/hotstuff/test_pacemaker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ namespace eosio { namespace hotstuff {

void pipe(const std::vector<test_pacemaker::hotstuff_message>& messages);

void duplicate(hotstuff_message_index msg_type);

void dispatch(std::string memo, int count, hotstuff_message_index msg_type = hs_all_messages);

std::vector<hotstuff_message> dispatch(std::string memo, hotstuff_message_index msg_type = hs_all_messages);
Expand Down
8 changes: 8 additions & 0 deletions libraries/hotstuff/qc_chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,14 @@ namespace eosio::hotstuff {
auto increment_version = fc::make_scoped_exit([this]() { ++_state_version; });

const hs_bitset& finalizer_set = _current_qc.get_active_finalizers();

// if a finalizer has already aggregated a vote signature for the current QC, just discard this vote
vector<name> finalizers = _pacemaker->get_finalizers();
fcecin marked this conversation as resolved.
Show resolved Hide resolved
for (size_t i=0; i<finalizers.size(); ++i)
if (finalizers[i] == vote.finalizer)
if (finalizer_set.test(i))
return;

if (finalizer_set.any())
_current_qc.set_active_agg_sig(fc::crypto::blslib::aggregate({_current_qc.get_active_agg_sig(), vote.sig }));
else
Expand Down
139 changes: 139 additions & 0 deletions libraries/hotstuff/test/test_hotstuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1162,5 +1162,144 @@ BOOST_AUTO_TEST_CASE(hotstuff_7) try {

} FC_LOG_AND_RETHROW();

BOOST_AUTO_TEST_CASE(hotstuff_8) try {

//test optimistic responsiveness (3 confirmations per block)
//same as hotstuff_1, but with a duplication of vote messages as a regression test for vote duplication filtering

test_pacemaker tpm;
tpm.connect(unique_replicas); // complete connection graph

hotstuff_test_handler ht;

ht.initialize_qc_chains(tpm, unique_replicas);

tpm.set_proposer("bpa"_n);
tpm.set_leader("bpa"_n);
tpm.set_next_leader("bpa"_n);
tpm.set_finalizers(unique_replicas);

auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; });
finalizer_state fs_bpa;
qcc_bpa->second->get_state(fs_bpa);
auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; });
finalizer_state fs_bpb;
qcc_bpb->second->get_state(fs_bpb);

ht.print_bp_state("bpa"_n, "");

tpm.set_current_block_id(ids[0]); //first block

tpm.beat(); //produce first block and associated proposal

tpm.dispatch(""); //send proposal to replicas (prepare on first block)

ht.print_bp_state("bpa"_n, "");

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));

// produce duplicate votes: should not fail the test if qc_chain is filtering duplicate votes.
// we cannot use pipe(dispatch()) here because pipe will append the duplicate votes like this to the pending message queue:
// abcdefghijklmnopqrstuabcdefghijklmnopqrstu
// however, after receiving 15 unique votes, the quorum is met and the duplicate votes are discared by the quorum rule.
// tpm.duplicate() will duplicate like this: aabbccddee...ssttuu, which will exercise the duplicate vote filter (bitset test).
tpm.duplicate(test_pacemaker::hs_vote);

tpm.dispatch(""); //send votes on proposal (prepareQC on first block)

tpm.dispatch(""); //send proposal to replicas (precommit on first block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));

tpm.dispatch(""); //propagating votes on new proposal (precommitQC on first block)

tpm.dispatch(""); //send proposal to replicas (commit on first block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));

tpm.dispatch(""); //propagating votes on new proposal (commitQC on first block)

tpm.dispatch(""); //send proposal to replicas (decide on first block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8"));

tpm.dispatch(""); //propagating votes on new proposal (decide on first block)

tpm.set_current_block_id(ids[1]); //second block

tpm.beat(); //produce second block and associated proposal

tpm.dispatch(""); //send proposal to replicas (prepare on second block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f"));

tpm.dispatch(""); //send votes on proposal (prepareQC on second block)

tpm.dispatch(""); //send proposal to replicas (precommit on second block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09"));

tpm.dispatch(""); //propagating votes on new proposal (precommitQC on second block)

tpm.dispatch(""); //send proposal to replicas (commit on second block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("ab04f499892ad5ebd209d54372fd5c0bda0288410a084b55c70eda40514044f3"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7"));

tpm.dispatch(""); //propagating votes on new proposal (commitQC on second block)

tpm.dispatch(""); //send proposal to replicas (decide on second block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("9eeffb58a16133517d8d2f6f90b8a3420269de3356362677055b225a44a7c151"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("ab04f499892ad5ebd209d54372fd5c0bda0288410a084b55c70eda40514044f3"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb"));

tpm.dispatch(""); //send proposal to replicas (decide on second block)

qcc_bpa->second->get_state(fs_bpa);
BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("9eeffb58a16133517d8d2f6f90b8a3420269de3356362677055b225a44a7c151"));
BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("9eeffb58a16133517d8d2f6f90b8a3420269de3356362677055b225a44a7c151"));
BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f"));
BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb"));

//check bpb as well
qcc_bpb->second->get_state(fs_bpb);
BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("ab04f499892ad5ebd209d54372fd5c0bda0288410a084b55c70eda40514044f3"));
BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f"));
BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb"));

BOOST_CHECK_EQUAL(fs_bpa.b_finality_violation.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));

} FC_LOG_AND_RETHROW();


BOOST_AUTO_TEST_SUITE_END()
12 changes: 12 additions & 0 deletions libraries/hotstuff/test/test_pacemaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ namespace eosio::hotstuff {
}
}

void test_pacemaker::duplicate(hotstuff_message_index msg_type) {
std::vector<test_pacemaker::hotstuff_message> dup;
for (const auto& msg_pair : _pending_message_queue) {
const auto& [sender_id, msg] = msg_pair;
size_t v_index = msg.index();
dup.push_back(msg_pair);
if (v_index == msg_type)
dup.push_back(msg_pair);
}
_pending_message_queue = dup;
fcecin marked this conversation as resolved.
Show resolved Hide resolved
}

std::vector<test_pacemaker::hotstuff_message> test_pacemaker::dispatch(std::string memo, hotstuff_message_index msg_type) {

std::vector<test_pacemaker::hotstuff_message> dispatched_messages;
Expand Down