-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GH-3 Add unittest for vote_processor. Modify vote_processor to make i…
…t easier to test.
- Loading branch information
Showing
5 changed files
with
255 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
#include <boost/test/unit_test.hpp> | ||
|
||
#include <eosio/chain/controller.hpp> | ||
#include <eosio/chain/vote_processor.hpp> | ||
#include <eosio/testing/tester.hpp> | ||
#include <fc/bitutil.hpp> | ||
#include <boost/signals2/signal.hpp> | ||
#include <latch> | ||
|
||
namespace std { | ||
std::ostream& operator<<(std::ostream& os, const eosio::chain::vote_message& v) { | ||
os << "vote_message{" << v.block_id << std::endl; | ||
return os; | ||
} | ||
std::ostream& operator<<(std::ostream& os, const eosio::chain::vote_status& v) { | ||
os << fc::reflector<eosio::chain::vote_status>::to_string(v) << std::endl; | ||
return os; | ||
} | ||
} | ||
|
||
namespace { | ||
|
||
using namespace eosio; | ||
using namespace eosio::chain; | ||
|
||
block_id_type make_block_id(uint32_t block_num) { | ||
block_id_type block_id; | ||
block_id._hash[0] &= 0xffffffff00000000; | ||
block_id._hash[0] += fc::endian_reverse_u32(block_num); | ||
return block_id; | ||
} | ||
|
||
bls_private_key bls_priv_key_0 = bls_private_key::generate(); | ||
bls_private_key bls_priv_key_1 = bls_private_key::generate(); | ||
bls_private_key bls_priv_key_2 = bls_private_key::generate(); | ||
std::vector<bls_private_key> bls_priv_keys{bls_priv_key_0, bls_priv_key_1, bls_priv_key_2}; | ||
|
||
auto create_genesis_block_state() { // block 2 | ||
signed_block_ptr block = std::make_shared<signed_block>(); | ||
|
||
block->producer = eosio::chain::config::system_account_name; | ||
auto pub_key = eosio::testing::base_tester::get_public_key( block->producer, "active" ); | ||
|
||
std::vector<finalizer_authority> finalizers; | ||
finalizers.push_back(finalizer_authority{.description = "first", .weight = 1, .public_key = bls_priv_keys.at(0).get_public_key()}); | ||
finalizers.push_back(finalizer_authority{.description = "first", .weight = 1, .public_key = bls_priv_keys.at(1).get_public_key()}); | ||
finalizers.push_back(finalizer_authority{.description = "first", .weight = 1, .public_key = bls_priv_keys.at(2).get_public_key()}); | ||
finalizer_policy new_finalizer_policy{.finalizers = finalizers}; | ||
qc_claim_t initial_if_claim { .block_num = 2, | ||
.is_strong_qc = false }; | ||
emplace_extension(block->header_extensions, instant_finality_extension::extension_id(), | ||
fc::raw::pack(instant_finality_extension{ initial_if_claim, new_finalizer_policy, {} })); | ||
|
||
producer_authority_schedule schedule = { 0, { producer_authority{block->producer, block_signing_authority_v0{ 1, {{pub_key, 1}} } } } }; | ||
auto genesis = std::make_shared<block_state>(); | ||
genesis->block = block; | ||
genesis->active_finalizer_policy = std::make_shared<finalizer_policy>(new_finalizer_policy); | ||
genesis->block->previous = make_block_id(1); | ||
genesis->active_proposer_policy = std::make_shared<proposer_policy>(proposer_policy{.proposer_schedule = schedule}); | ||
genesis->core = finality_core::create_core_for_genesis_block(1); | ||
genesis->block_id = genesis->block->calculate_id(); | ||
return genesis; | ||
} | ||
|
||
auto create_test_block_state(const block_state_ptr& prev) { | ||
static block_timestamp_type timestamp; | ||
timestamp = timestamp.next(); // each test block state will be unique | ||
signed_block_ptr block = std::make_shared<signed_block>(prev->block->clone()); | ||
block->producer = eosio::chain::config::system_account_name; | ||
block->previous = prev->id(); | ||
block->timestamp = timestamp; | ||
|
||
auto priv_key = eosio::testing::base_tester::get_private_key( block->producer, "active" ); | ||
auto pub_key = eosio::testing::base_tester::get_public_key( block->producer, "active" ); | ||
|
||
auto sig_digest = digest_type::hash("something"); | ||
block->producer_signature = priv_key.sign( sig_digest ); | ||
|
||
vector<private_key_type> signing_keys; | ||
signing_keys.emplace_back( std::move( priv_key ) ); | ||
|
||
auto signer = [&]( digest_type d ) { | ||
std::vector<signature_type> result; | ||
result.reserve(signing_keys.size()); | ||
for (const auto& k: signing_keys) | ||
result.emplace_back(k.sign(d)); | ||
return result; | ||
}; | ||
block_header_state bhs = *prev; | ||
bhs.header = *block; | ||
bhs.header.timestamp = timestamp; | ||
bhs.header.previous = prev->id(); | ||
bhs.header.schedule_version = block_header::proper_svnn_schedule_version; | ||
bhs.block_id = block->calculate_id(); | ||
|
||
auto bsp = std::make_shared<block_state>(bhs, | ||
deque<transaction_metadata_ptr>{}, | ||
deque<transaction_receipt>{}, | ||
std::optional<valid_t>{}, | ||
std::optional<quorum_certificate>{}, | ||
signer, | ||
block_signing_authority_v0{ 1, {{pub_key, 1}} }, | ||
digest_type{}); | ||
|
||
return bsp; | ||
} | ||
|
||
vote_message make_empty_message(const block_id_type& id) { | ||
vote_message vm; | ||
vm.block_id = id; | ||
return vm; | ||
} | ||
|
||
vote_message make_vote_message(const block_state_ptr& bsp) { | ||
vote_message vm; | ||
vm.block_id = bsp->id(); | ||
vm.strong = true; | ||
size_t i = bsp->block_num() % bls_priv_keys.size(); | ||
vm.finalizer_key = bls_priv_keys.at(i).get_public_key(); | ||
vm.sig = bls_priv_keys.at(i).sign({(uint8_t*)bsp->strong_digest.data(), (uint8_t*)bsp->strong_digest.data() + bsp->strong_digest.data_size()}); | ||
return vm; | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE(vote_processor_tests) | ||
|
||
BOOST_AUTO_TEST_CASE( vote_processor_test ) { | ||
boost::signals2::signal<void(const vote_signal_params&)> voted_block; | ||
|
||
uint32_t received_connection_id = 0; | ||
vote_status received_vote_status = vote_status::unknown_block; | ||
vote_message received_vote_message{}; | ||
|
||
std::unique_ptr<std::latch> signaled; | ||
std::mutex forkdb_mtx; | ||
std::map<block_id_type, block_state_ptr> forkdb; | ||
auto add_to_forkdb = [&](const block_state_ptr& bsp) { | ||
std::lock_guard g(forkdb_mtx); | ||
forkdb[bsp->id()] = bsp; | ||
}; | ||
|
||
voted_block.connect( [&]( const vote_signal_params& vote_signal ) { | ||
received_connection_id = std::get<0>(vote_signal); | ||
received_vote_status = std::get<1>(vote_signal); | ||
received_vote_message = std::get<2>(vote_signal); | ||
signaled->count_down(); | ||
} ); | ||
|
||
vote_processor_t vp{voted_block, [&](const block_id_type& id) -> block_state_ptr { | ||
std::lock_guard g(forkdb_mtx); | ||
return forkdb[id]; | ||
}}; | ||
vp.start(2, [](const fc::exception& e) { | ||
edump((e)); | ||
BOOST_REQUIRE(false); | ||
}); | ||
|
||
{ // empty fork db, block never found, never signaled | ||
vote_message vm1 = make_empty_message(make_block_id(1)); | ||
signaled = std::make_unique<std::latch>(1); | ||
vp.process_vote_message(1, vm1); | ||
for (size_t i = 0; i < 5; ++i) { | ||
BOOST_CHECK(!signaled->try_wait()); // not signaled because no block | ||
std::this_thread::sleep_for(std::chrono::milliseconds{5}); | ||
} | ||
BOOST_CHECK(vp.size() == 1); | ||
// move lib past block | ||
vp.notify_lib(2); | ||
for (size_t i = 0; i < 5; ++i) { | ||
if (vp.size() == 0) break; | ||
std::this_thread::sleep_for(std::chrono::milliseconds{5}); | ||
} | ||
BOOST_CHECK(vp.size() == 0); | ||
} | ||
{ // process a valid vote | ||
signaled = std::make_unique<std::latch>(1); | ||
auto gensis = create_genesis_block_state(); | ||
auto bsp = create_test_block_state(gensis); | ||
BOOST_CHECK_EQUAL(bsp->block_num(), 3); | ||
vote_message m1 = make_vote_message(bsp); | ||
add_to_forkdb(bsp); | ||
vp.process_vote_message(1, m1); | ||
// duplicate ignored | ||
vp.process_vote_message(1, m1); | ||
signaled->wait(); | ||
BOOST_CHECK(1 == received_connection_id); | ||
BOOST_CHECK(vote_status::success == received_vote_status); | ||
BOOST_CHECK(m1 == received_vote_message); | ||
} | ||
{ // process an invalid signature vote | ||
signaled = std::make_unique<std::latch>(1); | ||
auto gensis = create_genesis_block_state(); | ||
auto bsp = create_test_block_state(gensis); | ||
BOOST_CHECK_EQUAL(bsp->block_num(), 3); | ||
vote_message m1 = make_vote_message(bsp); | ||
m1.strong = false; // signed with strong_digest | ||
add_to_forkdb(bsp); | ||
vp.process_vote_message(1, m1); | ||
signaled->wait(); | ||
BOOST_CHECK(1 == received_connection_id); | ||
BOOST_CHECK(vote_status::invalid_signature == received_vote_status); | ||
BOOST_CHECK(m1 == received_vote_message); | ||
} | ||
{ // process two diff block votes | ||
signaled = std::make_unique<std::latch>(2); | ||
auto gensis = create_genesis_block_state(); | ||
auto bsp = create_test_block_state(gensis); | ||
auto bsp2 = create_test_block_state(bsp); | ||
vote_message m1 = make_vote_message(bsp); | ||
vote_message m2 = make_vote_message(bsp2); | ||
vp.process_vote_message(2, m1); | ||
vp.process_vote_message(2, m2); | ||
for (size_t i = 0; i < 5; ++i) { | ||
if (vp.size() == 2) break; | ||
std::this_thread::sleep_for(std::chrono::milliseconds{5}); | ||
} | ||
BOOST_CHECK(vp.size() == 2); | ||
add_to_forkdb(bsp); | ||
add_to_forkdb(bsp2); | ||
signaled->wait(); | ||
BOOST_CHECK(2 == received_connection_id); | ||
BOOST_CHECK(vote_status::success == received_vote_status); | ||
BOOST_CHECK(m1 == received_vote_message || m2 == received_vote_message); | ||
} | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() | ||
|
||
} |