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

[1.0.2 -> main] Test regression of syncing and replay with actual committed reference blockchain data #859

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
42abd8c
Add arguments_contains() and last_irreversible_block_id() to libtester
linh2931 Sep 26, 2024
b84f0d7
Create initial consensus_blockchain data
linh2931 Sep 26, 2024
8f17db5
Replay and sync with reference blockchain
linh2931 Sep 26, 2024
69e94d3
fix temp_dir going out of scope
linh2931 Sep 27, 2024
a984c91
Use fc::read_file_contents to read snapshot and reference ID files (w…
linh2931 Sep 27, 2024
faed7da
Calculate ref_blockchain_path in only one place
linh2931 Sep 27, 2024
43f19bb
Add more comments
linh2931 Sep 27, 2024
351f9b4
Use const std::string& for the argument of arguments_contains()
linh2931 Sep 30, 2024
2fd7c00
Remove spaces between std::filesystem:: and copy_options::overwrite_e…
linh2931 Sep 30, 2024
661dedf
Merge branch 'release/1.0' into replay_with_ref_blockchain_1_0_2
linh2931 Sep 30, 2024
ca64cab
Set default value of start_time in block_report constructor to now
linh2931 Oct 1, 2024
2ea937d
Use producing time for Produced block and applying time for Received …
linh2931 Oct 1, 2024
eced7d9
Add time unit us to the end of reported CPU time
linh2931 Oct 1, 2024
116512c
Set start_time for producing a block in correct place
linh2931 Oct 1, 2024
ebbf8d5
Use pending block_report.start_time for producing time calculation
linh2931 Oct 1, 2024
f0b93a9
Save blockchain data before validating block id (otherwise it won't b…
linh2931 Oct 1, 2024
9a3260e
Merge pull request #850 from AntelopeIO/fix_produced_time
linh2931 Oct 1, 2024
ca02daf
Merge pull request #827 from AntelopeIO/replay_with_ref_blockchain_1_0_2
linh2931 Oct 1, 2024
66efb04
Resolve merge conflicts
linh2931 Oct 1, 2024
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
8 changes: 8 additions & 0 deletions libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,13 @@ namespace eosio::testing {
return {cfg, gen};
}

static bool arguments_contains(const std::string& arg) {
auto argc = boost::unit_test::framework::master_test_suite().argc;
auto argv = boost::unit_test::framework::master_test_suite().argv;

return std::find(argv, argv + argc, arg) != (argv + argc);
}

// ideally, users of `tester` should not access the controller directly,
// so we provide APIs to access the chain head and fork_db head, and some
// other commonly used APIs.
Expand All @@ -529,6 +536,7 @@ namespace eosio::testing {
block_handle fork_db_head() const { return control->fork_db_head(); }

chain_id_type get_chain_id() const { return control->get_chain_id(); }
block_id_type last_irreversible_block_id() const { return control->last_irreversible_block_id(); }
uint32_t last_irreversible_block_num() const { return control->last_irreversible_block_num(); }
bool block_exists(const block_id_type& id) const { return control->block_exists(id); }

Expand Down
150 changes: 145 additions & 5 deletions unittests/savanna_misc_tests.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "savanna_cluster.hpp"
#include <savanna_cluster.hpp>
#include <test-data.hpp>

#include <fc/io/fstream.hpp> // for read_file_contents

using namespace eosio::chain;
using namespace eosio::testing;
Expand Down Expand Up @@ -591,18 +594,133 @@ BOOST_FIXTURE_TEST_CASE(validate_qc_requiring_finalizer_policies, savanna_cluste

} FC_LOG_AND_RETHROW()

static void save_blockchain_data(const std::filesystem::path& ref_blockchain_path,
const std::filesystem::path& blocks_path,
const block_id_type& id,
const std::string& snapshot) {
auto source_log_file = blocks_path / "blocks.log";
auto source_index_file = blocks_path / "blocks.index";

auto ref_log_file = ref_blockchain_path / "blocks.log";
auto ref_index_file = ref_blockchain_path / "blocks.index";
auto ref_id_file_name = ref_blockchain_path / "id";
auto ref_snapshot_file_name = ref_blockchain_path / "snapshot";

// save reference blocks log
std::filesystem::copy_file(source_log_file, ref_log_file, std::filesystem::copy_options::overwrite_existing);
std::filesystem::copy_file(source_index_file, ref_index_file, std::filesystem::copy_options::overwrite_existing);

// save reference block_id
fc::cfile ref_id_file;
ref_id_file.set_file_path(ref_id_file_name);
ref_id_file.open("wb");
ref_id_file.write(id.data(), id.data_size());
ref_id_file.close();

// save reference snapshot
fc::cfile snapshot_file;
snapshot_file.set_file_path(ref_snapshot_file_name);
snapshot_file.open("w");
snapshot_file.write(snapshot.data(), snapshot.size());
snapshot_file.close();
}

static block_id_type read_reference_id(const std::filesystem::path& ref_blockchain_path) {
auto ref_id_file_path = ref_blockchain_path / "id";
std::string content;
fc::read_file_contents(ref_id_file_path, content);

return block_id_type(content.data(), content.size());
}

static std::string read_reference_snapshot(const std::filesystem::path& ref_blockchain_path) {
auto ref_snapshot_file_path = ref_blockchain_path / "snapshot";
std::string content;
fc::read_file_contents(ref_snapshot_file_path, content);

return content;
}

// need to pass in temp_dir. otherwise it will be destroyed after replay_reference_blockchain returns
static std::unique_ptr<tester> replay_reference_blockchain(const std::filesystem::path& ref_blockchain_path,
const fc::temp_directory& temp_dir,
const block_log& blog) {
// replay the reference blockchain and make sure LIB id in the replayed
// chain matches reference LIB id
// --------------------------------------------------------------------
auto config = tester::default_config(temp_dir).first;

auto genesis = eosio::chain::block_log::extract_genesis_state(ref_blockchain_path);
BOOST_REQUIRE(genesis);

std::filesystem::create_directories(config.blocks_dir);

std::filesystem::copy(ref_blockchain_path / "blocks.log", config.blocks_dir / "blocks.log");
std::filesystem::copy(ref_blockchain_path / "blocks.index", config.blocks_dir / "blocks.index");

// do a full block invariants check
config.force_all_checks = true;

// replay the reference blockchain
std::unique_ptr<tester> replay_chain = std::make_unique<tester>(config, *genesis);

auto ref_lib_id = blog.head_id();
BOOST_REQUIRE_EQUAL(*ref_lib_id, replay_chain->last_irreversible_block_id());

return replay_chain;
}

static void sync_replayed_blockchain(const std::filesystem::path& ref_blockchain_path,
std::unique_ptr<tester>&& replay_chain,
const block_log& blog) {
tester sync_chain;
sync_chain.close(); // stop the chain

// remove state and blocks log so we can restart from snapshot
std::filesystem::remove_all(sync_chain.get_config().state_dir);
std::filesystem::remove_all(sync_chain.get_config().blocks_dir);

// restart from reference snapshot
sync_chain.open(buffered_snapshot_suite::get_reader(read_reference_snapshot(ref_blockchain_path)));

// sync with the replayed blockchain
while( sync_chain.fork_db_head().block_num() < replay_chain->fork_db_head().block_num() ) {
auto fb = replay_chain->fetch_block_by_number( sync_chain.fork_db_head().block_num()+1 );
sync_chain.push_block( fb );
}

// In syncing, use the head for checking as it advances further than LIB
auto head_block_num = sync_chain.head().block_num();
signed_block_ptr ref_block = blog.read_block_by_num(head_block_num);

BOOST_REQUIRE_EQUAL(ref_block->calculate_id(), sync_chain.head().id());
}

// ----------------------------------------------------------------------------------------------------
// For issue #694, we need to change the finality core of the `block_header_state`, but we want to
// ensure that this doesn't create a consensus incompatibility with Spring 1.0.0, so the blocks created
// with newer versions remain compatible (and linkable) by Spring 1.0.0.
// with newer versions remain compatible (and linkable) with blocks by Spring 1.0.0.
//
// This test adds a utility that saves reference blockchain data and checks for
// regression in compatibility of syncing and replaying the reference blockchain data.
//
// To save reference blockchain data in `unittests/test-data/consensus_blockchain`,
// run
// `unittests/unit_test -t savanna_misc_tests/verify_block_compatibitity -- --save-blockchain`
// ----------------------------------------------------------------------------------------------------
BOOST_FIXTURE_TEST_CASE(verify_spring_1_0_block_compatibitity, savanna_cluster::cluster_t) try {
BOOST_FIXTURE_TEST_CASE(verify_block_compatibitity, savanna_cluster::cluster_t) try {
using namespace savanna_cluster;
auto& A=_nodes[0];
const auto& tester_account = "tester"_n;
//_debug_mode = true;

bool save_blockchain = tester::arguments_contains("--save-blockchain");

std::string snapshot;
if (save_blockchain) { // take a snapshot at the beginning
snapshot = A.snapshot();
}

// update finalizer_policy with a new key for B
// --------------------------------------------
base_tester::finalizer_policy_input input;
Expand Down Expand Up @@ -681,10 +799,32 @@ BOOST_FIXTURE_TEST_CASE(verify_spring_1_0_block_compatibitity, savanna_cluster::
BOOST_REQUIRE_EQUAL(qc_s(qc(b9)), strong_qc(b8)); // b9 claims a strong QC on b8
BOOST_REQUIRE_EQUAL(A.lib_number, b6->block_num()); // b9 makes B6 final

// check that the block id of b9 match what we got with Spring 1.0.0
std::filesystem::path test_data_path { UNITTEST_TEST_DATA_DIR };
auto ref_blockchain_path = test_data_path / "consensus_blockchain";

// check that the block id of b9 match what we got before.
auto b9_id = b9->calculate_id();
BOOST_REQUIRE_EQUAL(b9_id, block_id_type{"00000013725f3d79bd4dd4091d0853d010a320f95240981711a942673ad87918"});

if (save_blockchain) {
save_blockchain_data(ref_blockchain_path, A.get_config().blocks_dir, b9_id, snapshot);
return;
}

// Do block id validation after we save blockchain data in case the id needs to be changed in future
BOOST_REQUIRE_EQUAL(b9_id, read_reference_id(ref_blockchain_path));

block_log blog(ref_blockchain_path);

// replay the reference blockchain and make sure LIB id in the replayed
// chain matches reference LIB id
// --------------------------------------------------------------------
fc::temp_directory temp_dir; // need to pass in temp_dir. otherwise it would be destroyed after replay_reference_blockchain returns
std::unique_ptr<tester> replay_chain = replay_reference_blockchain(ref_blockchain_path, temp_dir, blog);

// start another blockchain using reference snapshot, and sync with the blocks
// from the replayed blockchain
// ---------------------------------------------------------------------------
sync_replayed_blockchain(ref_blockchain_path, std::move(replay_chain), blog);
} FC_LOG_AND_RETHROW()

/* -----------------------------------------------------------------------------------------------------
Expand Down
Binary file not shown.
Binary file not shown.
Binary file added unittests/test-data/consensus_blockchain/id
Binary file not shown.
Binary file added unittests/test-data/consensus_blockchain/snapshot
Binary file not shown.