diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 4f9068ad0b..ffcc254dd9 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -375,7 +375,7 @@ namespace eosio::testing { // only block number is signaled, in forking tests will get the same block number more than once. }); [[maybe_unused]] auto accepted_block_header_connection = control->accepted_block_header().connect([this](const block_signal_params& t) { - const auto& [block, id] = t; + [[maybe_unused]] const auto& [block, id] = t; assert(block); assert(_check_signal(id, block_signal::accepted_block_header)); }); diff --git a/plugins/producer_plugin/test/test_block_timing_util.cpp b/plugins/producer_plugin/test/test_block_timing_util.cpp index 1c9108710e..dd53c809a7 100644 --- a/plugins/producer_plugin/test/test_block_timing_util.cpp +++ b/plugins/producer_plugin/test/test_block_timing_util.cpp @@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(test_calculate_producer_wake_up_time) { for (uint32_t i = 0; i < static_cast(config::producer_repetitions * active_schedule.size() * 3); ++i) { // 3 rounds to test boundaries block_timestamp_type block_timestamp(prod_round_1st_block_slot + i); auto block_time = block_timestamp.to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{block_time}); } } { // We have all producers in active_schedule configured, we should produce every block @@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(test_calculate_producer_wake_up_time) { for (uint32_t i = 0; i < static_cast(config::producer_repetitions * active_schedule.size() * 3); ++i) { // 3 rounds to test boundaries block_timestamp_type block_timestamp(prod_round_1st_block_slot + i); auto block_time = block_timestamp.to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{block_time}); } } { // We have all producers in active_schedule of 21 (plus a couple of extra producers configured), we should produce every block @@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(test_calculate_producer_wake_up_time) { for (uint32_t i = 0; i < static_cast(config::producer_repetitions * active_schedule.size() * 3); ++i) { // 3 rounds to test boundaries block_timestamp_type block_timestamp(prod_round_1st_block_slot + i); auto block_time = block_timestamp.to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{block_time}); } } { // Tests for when we only have a subset of all active producers, we do not produce all blocks, only produce blocks for our round @@ -194,48 +194,48 @@ BOOST_AUTO_TEST_CASE(test_calculate_producer_wake_up_time) { std::set producers = { "initb"_n }; block_timestamp_type block_timestamp(prod_round_1st_block_slot); auto expected_block_time = block_timestamp_type(prod_round_1st_block_slot + config::producer_repetitions).to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-1}, producers, active_schedule, empty_watermarks), expected_block_time); // same - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions-1}, producers, active_schedule, empty_watermarks), expected_block_time); // same - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions-2}, producers, active_schedule, empty_watermarks), expected_block_time); // same - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions-3}, producers, active_schedule, empty_watermarks), expected_block_time); // same + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-1}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // same + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions-1}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // same + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions-2}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // same + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions-3}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // same // current which gives same expected - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions}, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); expected_block_time += fc::microseconds(config::block_interval_us); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions+1}, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot+config::producer_repetitions+1}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // inita is first in the schedule, prod_round_1st_block_slot is block time of the first block, so will return the next block time as that is when current should be produced producers = std::set{ "inita"_n }; block_timestamp = block_timestamp_type{prod_round_1st_block_slot}; expected_block_time = block_timestamp.to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-1}, producers, active_schedule, empty_watermarks), expected_block_time); // same - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-2}, producers, active_schedule, empty_watermarks), expected_block_time); // same - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-3}, producers, active_schedule, empty_watermarks), expected_block_time); // same + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-1}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // same + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-2}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // same + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp_type{block_timestamp.slot-3}, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // same for (size_t i = 0; i < config::producer_repetitions; ++i) { expected_block_time = block_timestamp_type(prod_round_1st_block_slot+i).to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); block_timestamp = block_timestamp.next(); } expected_block_time = block_timestamp.to_time_point(); - BOOST_CHECK_NE(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); // end of round, so not the next + BOOST_CHECK_NE(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // end of round, so not the next // initc is third in the schedule, verify its wake-up time is as expected producers = std::set{ "initc"_n }; block_timestamp = block_timestamp_type(prod_round_1st_block_slot); // expect 2*producer_repetitions since we expect wake-up time to be after the first two rounds expected_block_time = block_timestamp_type(prod_round_1st_block_slot + 2*config::producer_repetitions).to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // inith, initk - configured for 2 of the 21 producers. inith is 8th in schedule, initk is 11th in schedule producers = std::set{ "inith"_n, "initk"_n }; block_timestamp = block_timestamp_type(prod_round_1st_block_slot); // expect to produce after 7 rounds since inith is 8th expected_block_time = block_timestamp_type(prod_round_1st_block_slot + 7*config::producer_repetitions).to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // give it a time after inith otherwise would return inith time block_timestamp = block_timestamp_type(prod_round_1st_block_slot + 8*config::producer_repetitions); // after inith round // expect to produce after 10 rounds since inith is 11th expected_block_time = block_timestamp_type(prod_round_1st_block_slot + 10*config::producer_repetitions).to_time_point(); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // cpu_effort at 50%, initc constexpr fc::microseconds half_cpu_effort = fc::microseconds{eosio::chain::config::block_interval_us / 2u}; @@ -243,18 +243,18 @@ BOOST_AUTO_TEST_CASE(test_calculate_producer_wake_up_time) { block_timestamp = block_timestamp_type(prod_round_1st_block_slot); expected_block_time = block_timestamp_type(prod_round_1st_block_slot + 2*config::producer_repetitions).to_time_point(); // first in round is not affected by cpu effort - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(half_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(half_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); block_timestamp = block_timestamp_type(prod_round_1st_block_slot + 2*config::producer_repetitions + 1); // second in round is 50% sooner expected_block_time = block_timestamp.to_time_point(); expected_block_time -= fc::microseconds(half_cpu_effort); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(half_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(half_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // third in round is 2*50% sooner block_timestamp = block_timestamp_type(prod_round_1st_block_slot + 2*config::producer_repetitions + 2); // second in round is 50% sooner expected_block_time = block_timestamp.to_time_point(); expected_block_time -= fc::microseconds(2*half_cpu_effort.count()); - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(half_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(half_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); } { // test watermark std::vector active_schedule{ // 21 @@ -270,15 +270,15 @@ BOOST_AUTO_TEST_CASE(test_calculate_producer_wake_up_time) { // initc, with no watermarks producers = std::set{ "initc"_n }; auto expected_block_time = block_timestamp_type(prod_round_1st_block_slot + 2*config::producer_repetitions).to_time_point(); // without watermark - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, empty_watermarks), std::optional{expected_block_time}); // add watermark at first block, first block should not be allowed, wake-up time should be after first block of initc prod_watermarks.consider_new_watermark("initc"_n, 2, block_timestamp_type((prod_round_1st_block_slot + 2*config::producer_repetitions + 1))); // +1 since watermark is in block production time expected_block_time = block_timestamp_type(prod_round_1st_block_slot + 2*config::producer_repetitions + 1).to_time_point(); // with watermark, wait until next - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, prod_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, prod_watermarks), std::optional{expected_block_time}); // add watermark at first 2 blocks, first & second block should not be allowed, wake-up time should be after second block of initc prod_watermarks.consider_new_watermark("initc"_n, 2, block_timestamp_type((prod_round_1st_block_slot + 2*config::producer_repetitions + 1 + 1))); expected_block_time = block_timestamp_type(prod_round_1st_block_slot + 2*config::producer_repetitions + 2).to_time_point(); // with watermark, wait until next - BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, prod_watermarks), expected_block_time); + BOOST_CHECK_EQUAL(calculate_producer_wake_up_time(full_cpu_effort, 2, block_timestamp, producers, active_schedule, prod_watermarks), std::optional{expected_block_time}); } { // actual example that caused multiple start blocks producer_watermarks prod_watermarks; diff --git a/unittests/finalizer_tests.cpp b/unittests/finalizer_tests.cpp index e44f228138..d0fc4ee5f5 100644 --- a/unittests/finalizer_tests.cpp +++ b/unittests/finalizer_tests.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include using namespace eosio; @@ -95,6 +96,16 @@ void set_fsi(my_finalizers_t& fset, const std::vector& keys, const F ((fset.set_fsi(keys[I].pubkey, fsi[I])), ...); } +// sleep for n periods of the file clock +// -------------------------------------- +void sleep_for_n_file_clock_periods(uint32_t n) { + using file_clock = std::chrono::file_clock; + auto n_periods = file_clock::duration(n); + auto sleep_duration = std::chrono::duration_cast(n_periods); + + std::this_thread::sleep_for(sleep_duration); +} + BOOST_AUTO_TEST_SUITE(finalizer_tests) BOOST_AUTO_TEST_CASE( basic_finalizer_safety_file_io ) try { @@ -228,32 +239,37 @@ BOOST_AUTO_TEST_CASE( finalizer_safety_file_io ) try { } FC_LOG_AND_RETHROW() +namespace fs = std::filesystem; -BOOST_AUTO_TEST_CASE( finalizer_safety_file_versioning ) try { - namespace fs = std::filesystem; +void create_fsi_reference(my_finalizers_t& fset) { + std::vector keys = create_keys(3); + std::vector fsi = create_random_fsi(3); - fs::path test_data_path { UNITTEST_TEST_DATA_DIR }; - auto fsi_reference_dir = test_data_path / "fsi"; + bls_pub_priv_key_map_t local_finalizers = create_local_finalizers<0, 1, 2>(keys); + fset.set_keys(local_finalizers); + set_fsi(fset, keys, fsi); +} - auto create_fsi_reference = [&](my_finalizers_t& fset) { - std::vector keys = create_keys(3); - std::vector fsi = create_random_fsi(3); +void create_fsi_reference_file(const fs::path& safety_file_path) { + my_finalizers_t fset{safety_file_path}; + create_fsi_reference(fset); + fset.save_finalizer_safety_info(); +} - bls_pub_priv_key_map_t local_finalizers = create_local_finalizers<0, 1, 2>(keys); - fset.set_keys(local_finalizers); - set_fsi(fset, keys, fsi); - }; +fs::path mk_versioned_fsi_file_path(uint32_t v) { + fs::path test_data_path { UNITTEST_TEST_DATA_DIR }; + auto fsi_reference_dir = test_data_path / "fsi"; - auto create_fsi_reference_file = [&](const fs::path& safety_file_path) { - my_finalizers_t fset{safety_file_path}; - create_fsi_reference(fset); - fset.save_finalizer_safety_info(); - }; + return fsi_reference_dir / ("safety_v"s + std::to_string(v) + ".dat"); +} - auto mk_versioned_fsi_file_path = [&](uint32_t v) { - return fsi_reference_dir / ("safety_v"s + std::to_string(v) + ".dat"); - }; +std::string read_file(const fs::path& path) { + std::string res; + fc::read_file_contents(path, res); + return res; +} +BOOST_AUTO_TEST_CASE( finalizer_safety_file_versioning ) try { auto current_version = my_finalizers_t::current_safety_file_version; // run this unittest with the option `-- --save-fsi-ref` to save ref file for the current version. @@ -288,7 +304,8 @@ BOOST_AUTO_TEST_CASE( finalizer_safety_file_versioning ) try { auto ref_path = mk_versioned_fsi_file_path(i); auto copy_path = tempdir.path() / ref_path.filename(); fs::copy_file(ref_path, copy_path, fs::copy_options::none); - std::this_thread::sleep_for(std::chrono::milliseconds{10}); + + sleep_for_n_file_clock_periods(2); // first load the reference file in the old format, and then save it in the new version format // ------------------------------------------------------------------------------------------- @@ -307,5 +324,52 @@ BOOST_AUTO_TEST_CASE( finalizer_safety_file_versioning ) try { } FC_LOG_AND_RETHROW() +// Verify that we have not changed the fsi file serialiization +// ------------------------------------------------------------ +BOOST_AUTO_TEST_CASE( finalizer_safety_file_serialization_unchanged ) try { + auto current_version = my_finalizers_t::current_safety_file_version; + auto ref_path = mk_versioned_fsi_file_path(current_version); // the saved file for current_version + + fc::temp_directory tempdir; + auto tmp_path = tempdir.path() / "new_safety.dat"; + create_fsi_reference_file(tmp_path); // save a new file in tmp_path + + BOOST_REQUIRE(read_file(ref_path) == read_file(tmp_path)); + +} FC_LOG_AND_RETHROW() + + +// Verify that the current version of safety.dat file committed to the repo can be loaded on +// nodeos startup (it is not saved until we actually vote, and voting would change the fsi). +// ----------------------------------------------------------------------------------------- +BOOST_AUTO_TEST_CASE( finalizer_safety_file_serialization_io ) try { + fc::temp_directory tempdir; + auto [cfg, genesis_state] = tester::default_config(tempdir); + + fs::path tmp_path = cfg.finalizers_dir / config::safety_filename; + + auto current_version = my_finalizers_t::current_safety_file_version; + fs::path ref_path = mk_versioned_fsi_file_path(current_version); // the saved file for current_version + + tester t( tempdir, true ); + + fs::create_directory(cfg.finalizers_dir); + fs::copy_file(ref_path, tmp_path, fs::copy_options::none); + auto initial_time = fs::last_write_time(tmp_path); + + sleep_for_n_file_clock_periods(2); + + // set finalizer, so that the file is overwritten. set the last one so that order is unchanged. + std::vector keys = create_keys(3); + bls_pub_priv_key_map_t local_finalizer_keys; + local_finalizer_keys[keys.back().pubkey_str] = keys.back().privkey_str; + t.control->set_node_finalizer_keys(local_finalizer_keys); + + // Since we didn't vote, the file time should not have changed. + auto last_time = fs::last_write_time(tmp_path); + BOOST_REQUIRE(last_time == initial_time); + +} FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END()